{"record":{"id":"add0674ad772ba08","repo":"nexu-io/open-design","slug":"openrouter-video-download-dlresp-status","errorCode":null,"errorMessage":"openrouter video download ${dlResp.status}","messagePattern":"openrouter video download (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/media/index.ts","lineNumber":2145,"sourceCode":"    );\n  }\n\n  // ── Step 3: Download the video binary ──────────────────────────────\n  // unsigned_urls are often third-party CDNs where sending our API key\n  // would leak credentials. However, sometimes OpenRouter returns a proxied\n  // openrouter.ai URL that still requires authorization. We only attach the\n  // auth header if the host is explicitly allowlisted as openrouter.ai.\n  const contentUrl = videoUrls[0]!;\n  const parsedContentUrl = new URL(contentUrl);\n\n  const dlHeaders: Record<string, string> = {};\n  if (parsedContentUrl.hostname === 'openrouter.ai') {\n    dlHeaders['authorization'] = `Bearer ${credentials.apiKey}`;\n  }\n\n  const dlResp = await fetch(contentUrl, withMediaRequestInit(ctx, { headers: dlHeaders }));\n  if (!dlResp.ok) {\n    throw new Error(`openrouter video download ${dlResp.status}`);\n  }\n  const arr = await dlResp.arrayBuffer();\n  const bytes = Buffer.from(arr);\n\n  return {\n    bytes,\n    providerNote: `openrouter/${wireModel} · ${aspectRatio} · ${bytes.length} bytes`,\n    suggestedExt: '.mp4',\n  };\n}\n\nfunction openRouterAspectFor(aspect?: string): string {\n  // OpenRouter normalises aspect ratios across providers. Our\n  // MEDIA_ASPECTS vocabulary is a strict subset — pass known values\n  // through, default to 16:9 for video.\n  if (\n    aspect === '1:1'\n    || aspect === '16:9'","sourceCodeStart":2127,"sourceCodeEnd":2163,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/media/index.ts#L2127-L2163","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst dlResp = await fetch(contentUrl, withMediaRequestInit(ctx, { headers: dlHeaders }));\nif (!dlResp.ok) {\n  throw new Error(`openrouter video download ${dlResp.status}`);\n}\n\n// after (caller-side retry wrapper)\nasync function fetchWithRetry(url: string, opts: RequestInit, attempts = 3) {\n  let last: Error | null = null;\n  for (let i = 0; i < attempts; i++) {\n    const r = await fetch(url, opts);\n    if (r.ok) return r;\n    last = new Error(`download ${r.status}`);\n    if (r.status >= 500 && i < attempts - 1) { await new Promise(res => setTimeout(res, 1000 * (i + 1))); continue; }\n    throw last;\n  }\n  throw last!;\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Download failures are dominated by transient CDN 5xx and expired signed URLs —\n// retry with backoff, but bail immediately on 4xx that won't self-heal.\nasync function downloadOpenRouterVideo(contentUrl: string, opts: RequestInit, attempts = 3): Promise<Buffer> {\n  let last: Error | null = null;\n  for (let i = 0; i < attempts; i++) {\n    const r = await fetch(contentUrl, opts);\n    if (r.ok) return Buffer.from(await r.arrayBuffer());\n    last = new Error(`openrouter video download ${r.status}`);\n    // Only retry on server errors; 4xx (auth/gone) need a new job, not a retry.\n    if (r.status >= 500 && i < attempts - 1) {\n      await new Promise(res => setTimeout(res, 1500 * (i + 1)));\n      continue;\n    }\n    break;\n  }\n  throw last!;\n}","preventionTips":["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."],"tags":["openrouter","video-generation","download","http-status","network"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}