{"record":{"id":"5c94c2245c0bbcc4","repo":"can1357/oh-my-pi","slug":"options-label-image-request-failed-resp-stat","errorCode":null,"errorMessage":"${options.label} image request failed (${resp.status}): ${message}","messagePattern":"(.+?) image request failed \\((.+?)\\): (.+?)","errorType":"http","errorClass":"ProviderHttpError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/image-gen.ts","lineNumber":481,"sourceCode":"\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${key}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\"User-Agent\": USER_AGENT,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(options.body),\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\t\t\tconst rawText = await resp.text();\n\t\t\tif (!resp.ok) {\n\t\t\t\tlet message = rawText;\n\t\t\t\ttry {\n\t\t\t\t\tconst parsedErr = JSON.parse(rawText) as { detail?: string; error?: { message?: string } };\n\t\t\t\t\tmessage = parsedErr.detail ?? parsedErr.error?.message ?? message;\n\t\t\t\t} catch {\n\t\t\t\t\t// Keep raw text.\n\t\t\t\t}\n\t\t\t\tthrow new ProviderHttpError(\n\t\t\t\t\t`${options.label} image request failed (${resp.status}): ${message}`,\n\t\t\t\t\tresp.status,\n\t\t\t\t\t{\n\t\t\t\t\t\theaders: resp.headers,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn rawText;\n\t\t},\n\t\t{ signal: options.signal },\n\t);\n}\n\n/** Decode an OpenAI-style images response (`{data: [{b64_json, url}]}`) into inline images. */\nasync function collectImageEndpointImages(\n\trawText: string,\n\tfetchImpl: FetchImpl,\n\tsignal: AbortSignal | undefined,","sourceCodeStart":463,"sourceCodeEnd":499,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/image-gen.ts#L463-L499","documentation":"postImageEndpointRequest wraps non-2xx HTTP responses from image provider endpoints (xAI, etc.) in a ProviderHttpError carrying the status, headers, and a message extracted from the JSON error body (`detail` or `error.message`) or the raw text. This is the provider rejecting the image request — authentication, billing, model access, or malformed request — and the message is the provider's own diagnostic.","triggerScenarios":"Any POST to the provider image endpoint returning non-2xx: invalid API key (401), insufficient credits (402/403), model not entitled (404), invalid params (400), or rate limits (429); raised via xaiRawText/rawText paths during image generation.","commonSituations":"Expired or rotated API keys in env/config; account without image-model access; exceeding rate limits or quota; wrong endpoint base URL or unsupported region.","solutions":["Read the embedded status and provider message to identify the exact rejection reason","For 401/403, verify and rotate the provider API key (env var / providers config)","For 429, add retry with backoff or raise the rate limit on the provider account","For 400, check the request params (prompt, model id, aspect ratio) against the provider's API docs","Confirm billing/credits are active on the provider account"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"function requireImageProviderKey(prov: string): void {\n\tconst key = process.env[`${prov.toUpperCase()}_API_KEY`];\n\tif (!key) throw new Error(`Missing API key for image provider '${prov}'`);\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait genImage({ prompt });\n} catch (err) {\n\tif (err instanceof ProviderHttpError) {\n\t\tif (err.status === 429 || err.status >= 500) return withBackoffRetry(() => genImage({ prompt }));\n\t\tif (err.status === 401 || err.status === 403) throw new Error(\"Check image provider API key/entitlements: \" + err.message);\n\t}\n\tthrow err;\n}","preventionTips":["Rotate and verify provider API keys in env/config before runs","Add backoff retries for 429/5xx image endpoint calls","Confirm the account has image-model entitlements/credits","Pin the provider base URL and region in config to avoid endpoint drift"],"tags":["http","provider-api","image-generation","api-key","rate-limit"],"backgroundTag":"provider-http-error","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}