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

  1. 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).
  2. Retry later or let the provider chain fall back if the code indicates rate limiting or a 5xx server error.
  3. Verify Codex OAuth credentials are valid and the ChatGPT account has web-search access by running a plain codex query.
  4. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/74bfec25fc4480c5. Report an issue: GitHub.