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

  1. Retry the render — transient CDN 5xx and expired signed URLs usually resolve on a second attempt.
  2. 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.
  3. 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).
  4. 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

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


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/add0674ad772ba08. Report an issue: GitHub.