{"record":{"id":"fc3a351a100d2b02","repo":"can1357/oh-my-pi","slug":"codex-live-signaling-failed-response-status","errorCode":null,"errorMessage":"Codex live signaling failed (${response.status}): ${detail}","messagePattern":"Codex live signaling failed \\((.+?)\\): (.+?)","errorType":"exception","errorClass":"LiveSignalingError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/live/transport.ts","lineNumber":224,"sourceCode":"\t\tconst headers = new Headers({\n\t\t\t...liveSessionHeaders(access, this.#options.sessionId, this.#realtimeSessionId, attestation),\n\t\t\tAccept: \"*/*\",\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t});\n\t\tconst fetchImpl = wrapFetchForProxy(fetch, LIVE_PROVIDER);\n\t\tconst response = await fetchImpl(SIGNALING_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders,\n\t\t\tbody: JSON.stringify({\n\t\t\t\tsdp: offer,\n\t\t\t\tsession: buildLiveSessionPayload(this.#options.instructions, this.#options.voice),\n\t\t\t}),\n\t\t\tsignal: this.#options.signal,\n\t\t});\n\t\tconst responseBody = await response.text();\n\t\tif (!response.ok) {\n\t\t\tconst detail = boundedErrorBody(responseBody, response.statusText);\n\t\t\tthrow new LiveSignalingError(response.status, `Codex live signaling failed (${response.status}): ${detail}`);\n\t\t}\n\t\tconst answer = responseBody;\n\t\tif (!answer.trim())\n\t\t\tthrow new LiveSignalingError(response.status, \"Codex live signaling returned an empty SDP answer\");\n\t\tconst callId = parseLiveCallId(response.headers.get(\"location\"));\n\t\tif (!callId) {\n\t\t\tthrow new LiveSignalingError(response.status, \"Codex live signaling returned no valid call ID\");\n\t\t}\n\t\treturn { answer, callId, access, attestation };\n\t}\n\n\tasync #connectSideband(callId: string, access: OAuthAccess, attestation: string | undefined): Promise<void> {\n\t\tlet failure = new Error(\"Codex live sideband connection failed\");\n\t\tfor (let attempt = 0; attempt < SIDEBAND_CONNECT_ATTEMPTS; attempt++) {\n\t\t\ttry {\n\t\t\t\tawait this.#openSideband(callId, access, attestation);\n\t\t\t\treturn;\n\t\t\t} catch (cause) {","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/live/transport.ts#L206-L242","documentation":"#signalWithAccess performs the WebRTC SDP offer/answer handshake with Codex live signaling over HTTP. When the HTTP response is not ok (4xx/5xx), it raises LiveSignalingError including the status code and a bounded snippet of the error body so the underlying rejection reason is visible.","triggerScenarios":"POSTing an SDP offer to the Codex live signaling endpoint and receiving a non-2xx response — expired/insufficient OAuth access, invalid offer body, missing attestation, rate limiting (429), or upstream outage (5xx).","commonSituations":"Expired ChatGPT/OAuth token, region or entitlement without live access, network proxy returning an error page, transient OpenAI-side 5xx during peak load.","solutions":["Read the status code and `detail` in the message — 401/403 means refresh OAuth credentials; 429 means back off and retry; 5xx means retry later.","Refresh the Codex OAuth access token (re-login).","Retry the signaling request with exponential backoff for 429/5xx.","Verify network/proxy settings allow reaching the signaling host."],"exampleFix":"// before\nconst { answer } = await transport.signal(offer); // raw LiveSignalingError\n// after\ntry {\n  const { answer } = await transport.signal(offer);\n} catch (e) {\n  if (e instanceof LiveSignalingError && (e.status === 429 || e.status >= 500)) await backoffRetry();\n  else if (e.status === 401) await refreshOAuthAndRetry();\n  else throw e;\n}","handlingStrategy":"retry","validationCode":"// pre-flight: ensure OAuth access exists and looks unexpired\nif (!access?.accessToken || access.expiresAt <= Date.now() + 60_000) {\n  await refreshAccess();\n}","typeGuard":"const isLiveSignalingError = (e: unknown): e is LiveSignalingError => e instanceof LiveSignalingError;","tryCatchPattern":"try {\n  await transport.signal(offer);\n} catch (e) {\n  if (e instanceof LiveSignalingError) {\n    if (e.status === 401 || e.status === 403) await refreshOAuthAndRetry();\n    else if (e.status === 429 || e.status >= 500) await retryWithBackoff();\n    else throw e;\n  } else throw e;\n}","preventionTips":["Refresh OAuth tokens before long live sessions","Add exponential backoff for 429/5xx signaling responses","Log the status + detail snippet from the error message","Verify proxy/network reachability to the signaling host"],"tags":["network","webrtc","http","signaling","codex"],"backgroundTag":"http-request-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}