{"record":{"id":"74bfec25fc4480c5","repo":"can1357/oh-my-pi","slug":"codex-request-failed-code-message-requ","errorCode":null,"errorMessage":"Codex request failed (${code}): ${message || \"Request failed\"}","messagePattern":"Codex request failed \\((.+?)\\): (.+?)","errorType":"exception","errorClass":"SearchProviderError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/web/search/providers/codex.ts","lineNumber":650,"sourceCode":"\t\t\t\t\t\tinputTokens: (resp.usage.input_tokens ?? 0) - cachedTokens,\n\t\t\t\t\t\toutputTokens: resp.usage.output_tokens ?? 0,\n\t\t\t\t\t\ttotalTokens: resp.usage.total_tokens ?? 0,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (eventType === \"error\") {\n\t\t\tconst { code, message } = extractCodexSseError(rawEvent);\n\t\t\tthrow new SearchProviderError(\n\t\t\t\t\"codex\",\n\t\t\t\t`Codex error (${code}): ${message || \"Unknown error\"}`,\n\t\t\t\tclassifyCodexSseErrorStatus(code, message),\n\t\t\t);\n\t\t} else if (eventType === \"response.failed\") {\n\t\t\tconst { code, message } = extractCodexSseError(rawEvent);\n\t\t\tconst detail = code\n\t\t\t\t? `Codex request failed (${code}): ${message || \"Request failed\"}`\n\t\t\t\t: `Codex request failed: ${message || \"Request failed\"}`;\n\t\t\tthrow new SearchProviderError(\"codex\", detail, classifyCodexSseErrorStatus(code, message));\n\t\t}\n\t}\n\n\tif (!webSearchInvoked) {\n\t\tthrow new CodexNoWebSearchError();\n\t}\n\n\tconst finalAnswer = answerParts.join(\"\\n\\n\").trim();\n\tconst streamedAnswer = streamedAnswerParts.join(\"\").trim();\n\t// Throw to advance the chain whenever Codex emitted nothing but image\n\t// placeholder prose — including the case where the streamed delta itself\n\t// is the placeholder (the model occasionally streams the same text it\n\t// publishes as the final output_text).\n\tconst finalIsPlaceholder = finalAnswer.length > 0 && isImagePlaceholderAnswer(finalAnswer);\n\tconst streamedIsPlaceholder = streamedAnswer.length > 0 && isImagePlaceholderAnswer(streamedAnswer);\n\tconst hasFinalText = finalAnswer.length > 0 && !finalIsPlaceholder;\n\tconst hasStreamedText = streamedAnswer.length > 0 && !streamedIsPlaceholder;\n\tif (!hasFinalText && !hasStreamedText && sources.length === 0) {","sourceCodeStart":632,"sourceCodeEnd":668,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/web/search/providers/codex.ts#L632-L668","documentation":"The Codex search provider streams the OpenAI Responses API over SSE. When the backend emits a `response.failed` event, callCodexSearch parses the embedded error code and message, maps it to an HTTP-like status via classifyCodexSseErrorStatus, and throws a SearchProviderError for provider \"codex\". The message surfaces the upstream error code (e.g. rate-limit, invalid-model) so the caller's provider fallback chain can decide whether to retry with another provider.","triggerScenarios":"The SSE stream from the Codex endpoint contains a `response.failed` event, which happens on upstream-side failures: unsupported model on the ChatGPT account, rate limiting, server errors, or malformed requests that the backend rejects mid-stream rather than at connection time.","commonSituations":"ChatGPT accounts not entitled to the selected Codex model (e.g. gpt-5-codex-mini); transient OpenAI capacity outages; expired or restricted ChatGPT sessions hitting throttles; custom endpoint misconfiguration that breaks the codex-rs request shape.","solutions":["Read the parenthesized code in the message and fix the underlying cause (e.g. switch model via PI_CODEX_WEB_SEARCH_MODEL if it is a model-unsupported 400).","Retry later or let the provider chain fall back if the code indicates rate limiting or a 5xx server error.","Verify Codex OAuth credentials are valid and the ChatGPT account has web-search access by running a plain codex query.","If using a custom endpoint, confirm it implements the codex-rs responses-lite request shape the transport expects."],"exampleFix":"// before\nmodel = \"gpt-5-codex-mini\"; // rejected on ChatGPT accounts\n// after\nexport PI_CODEX_WEB_SEARCH_MODEL=gpt-5.6 // account-safe model, or unset to use bundled defaults","handlingStrategy":"try-catch","validationCode":"import { getConfiguredModel } from \".../codex\";\nconst model = getConfiguredModel();\nif (model && !model.modelId) throw new Error(\"PI_CODEX_WEB_SEARCH_MODEL set but empty\");","typeGuard":"function isCodexUpstreamFailure(e: unknown): e is SearchProviderError & { provider: \"codex\" } {\n  return e instanceof SearchProviderError && e.provider === \"codex\" && /Codex request failed \\(\\w+\\)/.test(e.message);\n}","tryCatchPattern":"try {\n  return await searchCodex(params);\n} catch (e) {\n  if (isCodexUpstreamFailure(e)) {\n    if ((e.status ?? 0) >= 500 || (e.status ?? 0) === 429) return fallbackProvider(params);\n  }\n  throw e;\n}","preventionTips":["Use bundled default models instead of pinning PI_CODEX_WEB_SEARCH_MODEL to account-unsupported ids.","Keep Codex OAuth tokens fresh and verify account entitlement before heavy use.","Let the provider fallback chain handle transient 429/5xx codes instead of failing hard."],"tags":["network","sse","openai","codex","search-provider"],"backgroundTag":"upstream-sse-response-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}