can1357/oh-my-pi · error · SearchProviderError
Codex request failed (${code}): ${message || "Request failed
Error message
Codex request failed (${code}): ${message || "Request failed"} What it means
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.
Source
Thrown at packages/coding-agent/src/web/search/providers/codex.ts:650
inputTokens: (resp.usage.input_tokens ?? 0) - cachedTokens,
outputTokens: resp.usage.output_tokens ?? 0,
totalTokens: resp.usage.total_tokens ?? 0,
};
}
}
} else if (eventType === "error") {
const { code, message } = extractCodexSseError(rawEvent);
throw new SearchProviderError(
"codex",
`Codex error (${code}): ${message || "Unknown error"}`,
classifyCodexSseErrorStatus(code, message),
);
} else if (eventType === "response.failed") {
const { code, message } = extractCodexSseError(rawEvent);
const detail = code
? `Codex request failed (${code}): ${message || "Request failed"}`
: `Codex request failed: ${message || "Request failed"}`;
throw new SearchProviderError("codex", detail, classifyCodexSseErrorStatus(code, message));
}
}
if (!webSearchInvoked) {
throw new CodexNoWebSearchError();
}
const finalAnswer = answerParts.join("\n\n").trim();
const streamedAnswer = streamedAnswerParts.join("").trim();
// Throw to advance the chain whenever Codex emitted nothing but image
// placeholder prose — including the case where the streamed delta itself
// is the placeholder (the model occasionally streams the same text it
// publishes as the final output_text).
const finalIsPlaceholder = finalAnswer.length > 0 && isImagePlaceholderAnswer(finalAnswer);
const streamedIsPlaceholder = streamedAnswer.length > 0 && isImagePlaceholderAnswer(streamedAnswer);
const hasFinalText = finalAnswer.length > 0 && !finalIsPlaceholder;
const hasStreamedText = streamedAnswer.length > 0 && !streamedIsPlaceholder;
if (!hasFinalText && !hasStreamedText && sources.length === 0) {View on GitHub (pinned to 9690622007)
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.
Example fix
// before model = "gpt-5-codex-mini"; // rejected on ChatGPT accounts // after export PI_CODEX_WEB_SEARCH_MODEL=gpt-5.6 // account-safe model, or unset to use bundled defaults
Defensive patterns
Strategy: try-catch
Validate before calling
import { getConfiguredModel } from ".../codex";
const model = getConfiguredModel();
if (model && !model.modelId) throw new Error("PI_CODEX_WEB_SEARCH_MODEL set but empty"); Type guard
function isCodexUpstreamFailure(e: unknown): e is SearchProviderError & { provider: "codex" } {
return e instanceof SearchProviderError && e.provider === "codex" && /Codex request failed \(\w+\)/.test(e.message);
} Try / catch
try {
return await searchCodex(params);
} catch (e) {
if (isCodexUpstreamFailure(e)) {
if ((e.status ?? 0) >= 500 || (e.status ?? 0) === 429) return fallbackProvider(params);
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Codex returned a completion without running web search (no w
- Codex returned image-only response
- V2 remote compaction failed (${response.status} ${response.s
- V2 compaction stream closed before response.completed
- stream ended before message_start
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/74bfec25fc4480c5.
Report an issue: GitHub.