nexu-io/open-design · error · Error
openrouter image download ${imgResp.status}
Error message
openrouter image download ${imgResp.status} What it means
Thrown when OpenRouter returned an image as a hosted http(s) URL (rather than an inline base64 data URL) and the secondary fetch of that URL returned non-2xx. The code branches on a `data:image/...;base64,` regex first; only if the URL starts with 'http' does it fetch — so this fires only on the hosted-URL delivery path.
Source
Thrown at apps/daemon/src/media/index.ts:1906
}
const dataUrl: string | undefined = images[0]?.image_url?.url;
if (!dataUrl) {
throw new Error(
`openrouter image response missing image_url.url: ${truncate(text, 200)}`,
);
}
// Strip the data URL prefix (e.g. "data:image/png;base64,") and
// decode the remaining base64 payload.
const b64Match = dataUrl.match(/^data:image\/[^;]+;base64,(.+)$/s);
let bytes: Buffer;
if (b64Match) {
bytes = Buffer.from(b64Match[1]!, 'base64');
} else if (dataUrl.startsWith('http')) {
// Some models may return a plain URL instead of inline base64.
const imgResp = await fetch(dataUrl, withMediaRequestInit(ctx));
if (!imgResp.ok) throw new Error(`openrouter image download ${imgResp.status}`);
bytes = Buffer.from(await imgResp.arrayBuffer());
} else {
// Assume raw base64 without prefix.
bytes = Buffer.from(dataUrl, 'base64');
}
return {
bytes,
providerNote: `openrouter/${wireModel} · ${aspectRatio} · ${bytes.length} bytes`,
suggestedExt: sniffImageExt(bytes),
};
}
// ---------------------------------------------------------------------------
// OpenRouter's video API is a normalised, asynchronous interface sitting
// in front of multiple upstream providers (ByteDance Seedance 2.0,
// Google Veo 3.1, Alibaba Wan 2.7, etc.). The workflow mirrors the
// Grok / Volcengine pattern used elsewhere in this file:View on GitHub (pinned to 5be4028344)
Solutions
- Retry the render — hosted-URL expiry is the most common cause.
- If reproducible, attach the same authorization/attribution headers to the secondary fetch (currently withMediaRequestInit(ctx) carries ctx-level options).
- Check OpenRouter status for CDN incidents when the status is 5xx.
- Where possible prefer models/config that return inline base64 to avoid the secondary fetch entirely.
Example fix
// before
const imgResp = await fetch(dataUrl, withMediaRequestInit(ctx));
if (!imgResp.ok) throw new Error(`openrouter image download ${imgResp.status}`);
// after — one retry on 5xx with clearer status
let imgResp = await fetch(dataUrl, withMediaRequestInit(ctx));
if (!imgResp.ok && imgResp.status >= 500) {
await sleep(1000);
imgResp = await fetch(dataUrl, withMediaRequestInit(ctx));
}
if (!imgResp.ok) {
throw new Error(`openrouter image download ${imgResp.status} from ${truncate(dataUrl, 80)}`);
} Defensive patterns
Strategy: retry
Validate before calling
// Validate the hosted URL shape and freshness before fetching
function isOpenRouterImageUrl(u: unknown): u is string {
return typeof u === 'string' && /^https?:\/\//.test(u) && u.length < 8192;
} Type guard
function isRetryableDownloadStatus(status: number): boolean {
return status === 429 || status >= 500;
} Try / catch
let bytes: Buffer | undefined;
for (let attempt = 0; attempt < 2; attempt++) {
const r = await fetch(dataUrl, withMediaRequestInit(ctx));
if (r.ok) { bytes = Buffer.from(await r.arrayBuffer()); break; }
if (attempt === 0 && isRetryableDownloadStatus(r.status)) { await sleep(1000); continue; }
throw new Error(`openrouter image download ${r.status}`);
} Prevention
- Prefer inline base64 data URLs over hosted URLs to eliminate the secondary fetch.
- Fetch hosted URLs immediately — they often carry short TTLs.
- Carry auth/attribution headers into the secondary fetch in case the host requires them.
- Retry once on 5xx/429 before surfacing.
When it happens
Trigger: The hosted image URL expired before download, the CDN returned 403/404 because no referer/auth was attached, the host had a transient 5xx, or the URL is behind a rate-limited gateway.
Common situations: Slow networks where the signed URL TTL lapses, OpenRouter image-CDN incidents, or corporate proxies stripping required headers off the secondary fetch.
Related errors
- grok image fetch ${imgResp.status}
- openrouter image non-JSON response: ${truncate(text, 200)}
- nano-banana image non-JSON: ${truncate(text, 200)}
- no OpenRouter API key — configure it in Settings or set OPEN
- openrouter image ${resp.status}: ${truncate(text, 240)}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/780b267cf589a0e0.
Report an issue: GitHub.