{"record":{"id":"f6d8763fcb393e54","repo":"can1357/oh-my-pi","slug":"codex-returned-image-only-response","errorCode":null,"errorMessage":"Codex returned image-only response","messagePattern":"Codex returned image-only response","errorType":"exception","errorClass":"SearchProviderError","httpStatus":502,"severity":"warning","filePath":"packages/coding-agent/src/web/search/providers/codex.ts","lineNumber":669,"sourceCode":"\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) {\n\t\tthrow new SearchProviderError(\"codex\", \"Codex returned image-only response\", 502);\n\t}\n\tconst answer = hasFinalText ? finalAnswer : hasStreamedText ? streamedAnswer : \"\";\n\n\t// Fallback: when Codex omits url_citation annotations, scrape markdown links\n\t// and bare URLs from the synthesized answer so callers still receive sources.\n\tif (sources.length === 0 && answer.length > 0) {\n\t\tfor (const source of extractTextSources(answer)) {\n\t\t\taddSource(sources, source);\n\t\t}\n\t}\n\n\treturn {\n\t\tanswer,\n\t\tsources,\n\t\tmodel,\n\t\trequestId,\n\t\tusage,\n\t};","sourceCodeStart":651,"sourceCodeEnd":687,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/web/search/providers/codex.ts#L651-L687","documentation":"After filtering out image-placeholder prose (answers that only contain placeholder text for images instead of real content), if the provider produced no usable final or streamed answer text and collected zero sources, callCodexSearch throws a SearchProviderError with status 502. This indicates the model returned a response consisting only of images/placeholder content, giving the caller nothing to return as a search result.","triggerScenarios":"The Codex stream completes with web_search_call events but every output_text item (and streamed delta) matches isImagePlaceholderAnswer, and no url_citation annotations or web_search_call sources were collected.","commonSituations":"Model behavior quirks where the search response is dominated by generated images; backend changes that emit image placeholder prose instead of synthesized text; queries whose results are image-only.","solutions":["Retry the query — this is often transient model behavior; rephrase to request text-based findings.","Let the provider fallback chain advance by catching SearchProviderError and trying the next provider.","Update to a newer version if the backend changed output shape (placeholder detection may need updating).","Switch to a text-first provider (Brave, Tavily) for queries prone to image-only answers."],"exampleFix":"// before\nconst res = await searchCodex(params); // throws on image-only\n// after\ntry {\n  const res = await searchCodex(params);\n} catch (e) {\n  if (e instanceof SearchProviderError && e.status === 502) return fallbackProvider(params);\n  throw e;\n}","handlingStrategy":"fallback","validationCode":"// nothing to validate pre-call; validate post-hoc before consuming:\nfunction hasUsableResult(r: SearchResponse): boolean {\n  return Boolean(r.answer?.trim()) || r.sources.length > 0;\n}","typeGuard":"function isImageOnlyCodexResponse(e: unknown): e is SearchProviderError {\n  return e instanceof SearchProviderError && e.provider === \"codex\" && e.message === \"Codex returned image-only response\";\n}","tryCatchPattern":"try {\n  const res = await searchCodex(params);\n  if (!hasUsableResult(res)) return fallbackProvider(params);\n  return res;\n} catch (e) {\n  if (isImageOnlyCodexResponse(e)) return fallbackProvider(params);\n  throw e;\n}","preventionTips":["Treat this as transient and design search callers to advance the provider chain on 502.","Avoid queries that skew image-only, or append text-oriented directives.","Keep the package updated so placeholder detection tracks backend output changes."],"tags":["codex","openai","empty-response","image-placeholder","search-provider"],"backgroundTag":"empty-provider-response","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}