{"record":{"id":"0f101ecfe31fa826","repo":"pbakaus/impeccable","slug":"generate-image-api-error","errorCode":null,"errorMessage":"generate-image: API error {}: {}\n","messagePattern":"generate-image: API error (.+?): (.+?)\n","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/context/src/generate_image.rs","lineNumber":363,"sourceCode":"            .post(\"https://api.openai.com/v1/images/generations\")\n            .set(\"Authorization\", &format!(\"Bearer {}\", key))\n            .set(\"content-type\", \"application/json\")\n            .send_string(&serde_json::to_string(&Value::Object(m)).unwrap())\n    };\n    let (status, text) = match response {\n        Ok(r) => {\n            let st = r.status();\n            (st, r.into_string().unwrap_or_default())\n        }\n        Err(ureq::Error::Status(code, r)) => (code, r.into_string().unwrap_or_default()),\n        Err(e) => {\n            io.err(&format!(\"TypeError: fetch failed: {}\\n\", e));\n            return 1;\n        }\n    };\n    if !(200..300).contains(&status) {\n        let snippet: String = text.chars().take(300).collect();\n        io.err(&format!(\"generate-image: API error {}: {}\\n\", status, snippet));\n        return 1;\n    }\n    let json: Value = serde_json::from_str(&text).unwrap_or(Value::Null);\n    let b64 = json.get(\"data\").and_then(|d| d.get(0)).and_then(|d| d.get(\"b64_json\")).and_then(|b| b.as_str()).filter(|s| !s.is_empty());\n    let Some(b64) = b64 else {\n        io.err(\"generate-image: no image in response\\n\");\n        return 1;\n    };\n    let bytes = base64_decode(b64);\n    let _ = std::fs::write(abs(&out), bytes);\n    // best-effort embed + sidecar\n    // JS-PARITY: generate-image.mjs#676 reports whether the embed actually\n    // succeeded. The install-path-with-spaces half of #676 is a JS-only\n    // subprocess concern (fileURLToPath vs URL.pathname); the engine embeds\n    // in-process, so only the success tracking and message carry over here.\n    let embedded;\n    {\n        let mut sub_io = Io::captured(\"\", io.cwd.clone(), io.env.clone()).0;","sourceCodeStart":345,"sourceCodeEnd":381,"githubUrl":"https://github.com/pbakaus/impeccable/blob/2bc2879276c1f321a53c4ca99d3371e411329b52/crates/context/src/generate_image.rs#L345-L381","documentation":"The OpenAI image API returned an HTTP status outside 200..299. generate-image prints the status plus the first 300 characters of the response body and exits 1. This surfaces API-side rejections such as bad requests, authentication failures, or rate limits.","triggerScenarios":"POST to the images endpoint returns 4xx/5xx: 401 for an invalid/revoked OPENAI_API_KEY, 400 for an invalid size/quality/parameter combination, 429 for rate limiting or quota exhaustion, 5xx for OpenAI outages.","commonSituations":"Expired or rotated API keys; requesting a size not supported by the model; free-tier quota exhausted; org billing issue; transient OpenAI incidents; sending reference images that are too large.","solutions":["Read the status and body snippet in the message: 401 → fix the API key; 429 → wait/retry with backoff; 400 → correct size/quality/ref parameters.","Verify OPENAI_API_KEY is valid and has billing/quota enabled for the org.","Reduce or validate inputs (smaller size, supported quality value, fewer/smaller reference images) and retry.","Check the OpenAI status page for ongoing incidents if you see 5xx.","Implement retry with exponential backoff for 429/5xx responses in any wrapper scripts."],"exampleFix":"// before\nimpeccable generate-image --size 9999x9999 --prompt \"...\" --out o.png\n// generate-image: API error 400: ...\n// after\nimpeccable generate-image --size 1536x1024 --prompt \"...\" --out o.png","handlingStrategy":"retry","validationCode":"// preflight the key and known-good parameters\nif (!process.env.OPENAI_API_KEY?.startsWith(\"sk-\")) throw new Error(\"invalid OPENAI_API_KEY\");\nconst ALLOWED_SIZES = [\"1024x1024\", \"1536x1024\", \"1024x1536\"];\nif (!ALLOWED_SIZES.includes(size)) throw new Error(`unsupported size: ${size}`);","typeGuard":null,"tryCatchPattern":"const r = spawnSync(\"impeccable\", [\"generate-image\", ...], { encoding: \"utf8\" });\nif (r.status !== 0) {\n  const m = r.stderr.match(/API error (\\d+)/);\n  if (m && (m[1] === \"429\" || m[1].startsWith(\"5\"))) {\n    await backoffAndRetry();\n  } else if (m && m[1] === \"401\") {\n    throw new Error(\"OPENAI_API_KEY rejected — rotate/fix the key\");\n  } else throw new Error(r.stderr);\n}","preventionTips":["Map HTTP status to action: 401 fix key, 400 fix params, 429 backoff, 5xx retry/status page.","Keep request parameters (size, quality) within the model's supported values.","Monitor org billing/quota before large generation batches.","Retry 429/5xx with exponential backoff and jitter."],"tags":["http","api","openai"],"backgroundTag":"http-error-response","analyzedSha":"2bc2879276c1f321a53c4ca99d3371e411329b52","analyzedAt":"2026-09-08T04:51:14.109Z","contentChangedAt":"2026-09-08T04:51:14.109Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}