Skill v1.0.1
currentAutomated scan100/100+2 new
version: "1.0.1" name: challenge-troubleshoot description: Use when something in the Simulation Challenge pipeline is misbehaving — auth errors, agent disconnects, jobs stuck in Pending, jobs ending in Failed, drain frames. Maps a symptom to its likely cause and the next command to run.
challenge-troubleshoot — Symptom → cause → next command
When the user reports a problem, do this in order:
- Reproduce / verify the symptom with a read-only call (
/jobs,/result,/log,/current-user-info) before doing anything destructive. For "what's the status" questions,/jobsis the only endpoint that carries one. - Match the symptom to the table below.
- Hand the user the next command from the Action column. Do NOT auto-resubmit jobs (quota cost) or auto-kill agents (loses in-flight cases) without explicit confirmation.
Diagnostic heuristic: 4xx is never a network blip
A structured {"status":"error", ...} body with HTTP 4xx (400/401/403/404) is a semantic rejection by the platform — the request reached the server and was refused on its merits. Retrying it without changing the request gets the same answer and, for POST /api/challenge/job, burns a daily submission slot each time.
Only these qualify as transient and may be retried with backoff:
- TCP connection reset / timeout / DNS failure (curl exits non-zero before getting a response)
- HTTP
5xx(server-side, signals a transient backend failure)
If the user says "可能是网络抖动 / maybe a network blip" but you have a 4xx body in hand, push back: name the actual error and point at the table row.
Diagnostic heuristic: 5xx is often a missing / wrong token, not a real platform outage
curl -fsS collapses any HTTP 5xx to a terse (22) The requested URL returned error: 5XX and hides the body. Before concluding the platform is down, check this in order:
- State file sourced? Each Bash call should start with
[ -f ~/.simubotix-challenge.env ] && . ~/.simubotix-challenge.env— AI assistants spawn every command in a new subshell, so plainexportfrom a previous call is gone. - Token presence after sourcing.
echo "len=${#CHALLENGE_TOKEN}"— if it's 0, either the file doesn't exist (re-runchallenge-loginStep 1) or the file exists but doesn't contain the key (the previous login silently failed — seechallenge-loginStep 1's status check). - Token shape. Should be a 3-segment JWT (two dots).
echo "$CHALLENGE_TOKEN" | head -c 20; echo— gibberish ornullmeans a previous step persisted a bad value. - Body of the actual response. Drop
-fso curl prints the body even on non-2xx:
``bash [ -f ~/.simubotix-challenge.env ] && . ~/.simubotix-challenge.env curl -sS -i "$BASE_URL/api/challenge/current-user-info" \ -H "Authorization: Bearer $CHALLENGE_TOKEN" | tail -15 `` The body usually says exactly what's wrong (often a 401-style message returned with a 500 status code).
Only after the token is confirmed valid (e.g. the same value just succeeded against /login) should you treat the 5xx as a real platform issue and retry with backoff.
Symptom table
| # | Symptom | Likely cause | Action | |
|---|---|---|---|---|
| 1 | POST /api/challenge/job returns HTTP 400 invalid board / no task templates for board | config.board is missing or not in the currently-supported set | Today the supported values are instruction (default), spatial, manip, robust. Use one of those and resubmit. Do not retry the same wrong board — 400 is a semantic rejection, retrying burns a daily submission slot each time. This holds even when the user says "maybe a network blip" — see the heuristic above. If a contestant insists a new board exists, verify with the organizers first; challenge-submit-job Step 2 is the only list to trust. | |
| 2 | POST /api/challenge/job returns an "upload limit" error / /api/challenge/submission/quota returns remaining: 0 | Today's submission quota is used up | Wait until Beijing midnight (UTC+8), or work with an existing job. Report the actual used / limit from the endpoint — don't quote a cap from memory. | |
| 3 | Any plain HTTP /api/challenge/* call (login, result, log, job, tunnel-endpoint) returns 401 | Invalid / expired CHALLENGE_TOKEN | challenge-login Step 3 to refresh; if refresh also 401s, fall back to Step 1 (email + password). This only affects the plain HTTP APIs — it does not touch a JOB_TOKEN-based tunnel dial. | |
| 3b | The WS handshake (GET /api/challenge/tunnel) returns 401 | When dialing with JOB_TOKEN (the default per challenge-run-agent), the handshake does not 401 on token expiry — job_tokens don't expire. A 401 here means the ?job= UUID is not owned by this account, the job is in a terminal state, or the ?job= doesn't match the job the token was issued for. If still dialing with CHALLENGE_TOKEN (no JOB_TOKEN available, e.g. an older platform build), a 401 can additionally mean the login token itself expired | Verify JOB_UUID / ?job= came from this account's POST /api/challenge/job and that the job isn't terminal. Only if dialing with CHALLENGE_TOKEN, also try challenge-login Step 3 to refresh. | |
| 4 | Agent connects, then immediately disconnects | Per-user parallelism cap exceeded — too many agents online | Reduce K in challenge-run-agent, or wait for older agents to finish. | |
| 5 | Job stuck at detailed_status: pending / inference_disconnected, or progress not advancing for minutes | No agent online, or all agents dropped past the 30 s window | ps/check the agent processes; relaunch via challenge-run-agent if needed. `queued` is different — it means the cluster has no free GPU; the agent is fine and you must keep it online. | |
| 6 | Agent disconnected unexpectedly mid-run | Network blip | Within the gateway-side 30 s reconnect window, tunnel.sh redials automatically with the same agent_id to resume the open session. Do not manually relaunch in that window — you'd race the automatic redial and the gateway will reject the duplicate. Past 30 s the open session is gone; resubmitting a new job costs a daily submission slot — confirm before suggesting it. | |
| 7 | Received drain control frame | Platform finished dispatching cases and is asking for graceful shutdown | Let in-flight sessions finish, close the socket, and do NOT reconnect. | |
| 8 | Job ends at detailed_status: failed (list status = 4) | A case exhausted its retries, which kills the whole job | GET /api/challenge/job/$JOB_ID/log (see challenge-poll-result). Read `failure_reason` before any log text: 5/6/7/8 are platform-side teardowns, not your bug — 8 in particular means a different case was the root cause. 9 (tunnel lost) is the opposite — it usually means your agent dropped the WebSocket; check result.error for the sub-reason and see rows #5/#6 above. For 1/3/4, fetch result.stdout (usually a presigned URL, not inline text) and read the traceback. Common causes: handler exception during warmup (row #11), OOM, action-encoding mismatch. Fix the bug locally before resubmitting — resubmission costs a daily submission slot. | |
| 9 | /api/challenge/tunnel/endpoint returns empty string | Gateway not yet ready | Fall back to the fixed default ws://120.92.88.78/api/challenge/tunnel; the endpoint may simply be omitted because the host never changes. | |
| 10 | parallelism in the job response is 0 | User's MaxConcurrentCases is misconfigured (not "exhausted" — exhaustion is a runtime gateway check, not a response field) | Stop and surface to organizers; do not launch agents. | |
| 11 | Agent stuck in WARMUP, never reaches RUNNING — or the job ends Failed with a traceback referencing frame_bytes=b'' / zero-length decode in the /log output | Inference handler errored on the warmup empty frame | The warmup call passes an empty frame — handlers must tolerate that (e.g. short-circuit when len(frame_bytes) == 0 and return a no-op action). Fix in the inference repo, then resubmit (mind quota). | |
| 12 | tunnel.sh exits with code 1 immediately | Bad arguments / handler import failure / reconnect retries exhausted | Re-check <gpu_index> <job_uuid> <gateway_url> argument order; check inference-repo handler import path. | |
| 13 | tunnel.sh exits with code 130 | Ctrl-C | Expected; user-driven. | |
| 14 | POST /api/challenge/job returns HTTP 400 paper_link is not a valid URL / paper_link too long | Optional paper_link field is malformed or > 512 chars | Fix the URL or omit the field entirely; resubmit. Consumes a submission slot only if the call gets past validation. |
Diagnostics shortlist
Run these in order when triaging an unclear failure:
# 1. Token still valid?curl -fsS "$BASE_URL/api/challenge/current-user-info" \-H "Authorization: Bearer $CHALLENGE_TOKEN" | jq# 2. Job exists, what status? (status lives on the LIST — /result has no status field)curl -fsS "$BASE_URL/api/challenge/jobs?page=1&per-page=100" \-H "Authorization: Bearer $CHALLENGE_TOKEN" \| jq -c --argjson id "$JOB_ID" '.items[] | select(.id == $id)| {id, status, detailed_status, progress, score}'# 3. If detailed_status == failed, get the log (read failure_reason first)curl -fsS "$BASE_URL/api/challenge/job/$JOB_ID/log" \-H "Authorization: Bearer $CHALLENGE_TOKEN" | jq# 4. Scores, once terminalcurl -fsS "$BASE_URL/api/challenge/job/$JOB_ID/result" \-H "Authorization: Bearer $CHALLENGE_TOKEN" | jq# 5. Gateway endpoint sane?curl -fsS "$BASE_URL/api/challenge/tunnel/endpoint" \-H "Authorization: Bearer $CHALLENGE_TOKEN"
$JOB_ID must be the numeric id; the UUID belongs only to the tunnel dial. Passing a UUID to these paths yields 400 invalid job_id, which curl -fsS hides behind a bare exit code 22.
Things NOT to do
- The website/API domain is
robocoliseum.ai, but the gateway host is fixed at `120.92.88.78` and is a separate host:BASE_URL=https://robocoliseum.ai,TUNNEL_ENDPOINT=ws://120.92.88.78/api/challenge/tunnel. Do not "fix" the tunnel URL by swapping in the website domain. Prefer atunnel_endpointfrom the job response if present, but the fixed default is a safe fallback. - Do not reconnect after a
drain— the platform is finishing up, and the connection will be rejected. - Do not spam
POST /api/challenge/jobto "retry" — each call consumes one of the day's submission slots. Remaining quota is not authorization to guess (e.g. guessing aboardbecause "quota 还有"). Skill rules forbid the guess itself, independent of remaining slots. - Do not state the daily cap from memory. Read
GET /api/challenge/submission/quotaand quote itslimit/used/remaining; the limit is a platform setting and has changed before. - Do not kill running agents to "free a slot" without checking the job list first; you may be killing the agent that's about to finish your last case.
- Do not retry a 4xx as if it were a network blip — see the diagnostic heuristic at the top of this file.
- Do not poll
/resultfor status — it returns only{tasks, total}. A loop that branches on.statusthere never terminates. Usedetailed_statusfromGET /api/challenge/jobs.
Reference
For the WS wire protocol (dial, control frames, binary frame layout, state machine, reconnect, heartbeat), see the appendix of `challenge-run-agent` — it is inlined there in full. Most contestants don't need it; ./scripts/tunnel.sh from the inference repo already implements it. For the observation/action payload schema inside the data frames, see challenge-inference-protocol.