nexu-io/open-design · error · Error
openrouter video download ${dlResp.status}
Error message
openrouter video download ${dlResp.status} What it means
Thrown after OpenRouter returns completed video URLs but the subsequent GET to download the binary from videoUrls[0] returned a non-2xx HTTP status. The download path attaches the Bearer apiKey only when the host is openrouter.ai; third-party CDN hosts are fetched anonymously. The message reports the raw status code so you can distinguish 401/403 (auth) from 404/410 (gone) from 5xx (CDN fault).
Source
Thrown at apps/daemon/src/media/index.ts:2145
);
}
// ── Step 3: Download the video binary ──────────────────────────────
// unsigned_urls are often third-party CDNs where sending our API key
// would leak credentials. However, sometimes OpenRouter returns a proxied
// openrouter.ai URL that still requires authorization. We only attach the
// auth header if the host is explicitly allowlisted as openrouter.ai.
const contentUrl = videoUrls[0]!;
const parsedContentUrl = new URL(contentUrl);
const dlHeaders: Record<string, string> = {};
if (parsedContentUrl.hostname === 'openrouter.ai') {
dlHeaders['authorization'] = `Bearer ${credentials.apiKey}`;
}
const dlResp = await fetch(contentUrl, withMediaRequestInit(ctx, { headers: dlHeaders }));
if (!dlResp.ok) {
throw new Error(`openrouter video download ${dlResp.status}`);
}
const arr = await dlResp.arrayBuffer();
const bytes = Buffer.from(arr);
return {
bytes,
providerNote: `openrouter/${wireModel} · ${aspectRatio} · ${bytes.length} bytes`,
suggestedExt: '.mp4',
};
}
function openRouterAspectFor(aspect?: string): string {
// OpenRouter normalises aspect ratios across providers. Our
// MEDIA_ASPECTS vocabulary is a strict subset — pass known values
// through, default to 16:9 for video.
if (
aspect === '1:1'
|| aspect === '16:9'View on GitHub (pinned to 5be4028344)
Solutions
- Retry the render — transient CDN 5xx and expired signed URLs usually resolve on a second attempt.
- If status is 401/403 and the host is openrouter.ai, rotate/refresh the OpenRouter API key in Settings and confirm it has video-generation scope.
- Inspect providerNote/last job logs to confirm the contentUrl host; if it is a third-party CDN, verify the daemon host can reach it (egress firewall).
- If the URL is consistently gone (404/410), the upstream job produced a dead link — file an OpenRouter-side issue or switch video provider.
Example fix
// before
const dlResp = await fetch(contentUrl, withMediaRequestInit(ctx, { headers: dlHeaders }));
if (!dlResp.ok) {
throw new Error(`openrouter video download ${dlResp.status}`);
}
// after (caller-side retry wrapper)
async function fetchWithRetry(url: string, opts: RequestInit, attempts = 3) {
let last: Error | null = null;
for (let i = 0; i < attempts; i++) {
const r = await fetch(url, opts);
if (r.ok) return r;
last = new Error(`download ${r.status}`);
if (r.status >= 500 && i < attempts - 1) { await new Promise(res => setTimeout(res, 1000 * (i + 1))); continue; }
throw last;
}
throw last!;
} Defensive patterns
Strategy: retry
Try / catch
// Download failures are dominated by transient CDN 5xx and expired signed URLs —
// retry with backoff, but bail immediately on 4xx that won't self-heal.
async function downloadOpenRouterVideo(contentUrl: string, opts: RequestInit, attempts = 3): Promise<Buffer> {
let last: Error | null = null;
for (let i = 0; i < attempts; i++) {
const r = await fetch(contentUrl, opts);
if (r.ok) return Buffer.from(await r.arrayBuffer());
last = new Error(`openrouter video download ${r.status}`);
// Only retry on server errors; 4xx (auth/gone) need a new job, not a retry.
if (r.status >= 500 && i < attempts - 1) {
await new Promise(res => setTimeout(res, 1500 * (i + 1)));
continue;
}
break;
}
throw last!;
} Prevention
- Download the video bytes immediately after the poll completes — do not queue or sleep between completion and fetch, or signed URLs can expire.
- Make sure daemon egress is allowlisted for the OpenRouter CDN hosts, not just api.openrouter.ai.
- Log the contentUrl host on download failure so you can correlate which CDN a 5xx came from.
When it happens
Trigger: renderOpenRouterVideo reaches Step 3 (Download the video binary), fetch(contentUrl, ...) returns dlResp.ok === false. Happens when the returned URL is on openrouter.ai but the key lacks scope, when a third-party CDN URL expired between completion and download, or when the CDN returns a transient 5xx.
Common situations: API key revoked or rotated between submit and download (401/403 on openrouter.ai host); CDN link TTL expired because the daemon paused before fetching; network egress blocked to the CDN host; intermittent CDN 503/504.
Related errors
- openrouter poll ${pollResp.status}: ${truncate(pollText, 240
- openrouter video submit ${submitResp.status}: ${truncate(sub
- openrouter video non-JSON: ${truncate(submitText, 200)}
- openrouter poll non-JSON: ${truncate(pollText, 200)}
- download ${url} failed: ${resp.status} ${resp.statusText}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/add0674ad772ba08.
Report an issue: GitHub.