vercel-labs/skills · error · Error

Download failed with HTTP ${response.status}

Error message

Download failed with HTTP ${response.status}

What it means

downloadToFile issues a fetch with redirect:'follow' and a timeout, and throws when response.ok is false — any HTTP status outside 2xx aborts the download before it starts. The status code is embedded in the message, so a 404 means the URL is wrong, 403/401 means auth or rate limiting, and 5xx means the origin failed.

Source

Thrown at src/download-source.ts:91

  if (state.bytes > limits.extractMaxBytes) {
    throw new ArchiveValidationError(
      `Archive extracts to more than ${limits.extractMaxBytes} bytes. Set SKILLS_EXTRACT_MAX_BYTES to override.`
    );
  }
}

async function downloadToFile(
  url: string,
  targetFile: string,
  limits: DownloadLimits
): Promise<void> {
  const response = await fetch(url, {
    signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
    redirect: 'follow',
  });

  if (!response.ok) {
    throw new Error(`Download failed with HTTP ${response.status}`);
  }

  const contentLength = response.headers.get('content-length');
  if (contentLength) {
    const parsed = Number.parseInt(contentLength, 10);
    if (Number.isFinite(parsed) && parsed > limits.downloadMaxBytes) {
      throw new Error(
        `Download is larger than ${limits.downloadMaxBytes} bytes. Set SKILLS_DOWNLOAD_MAX_BYTES to override.`
      );
    }
  }

  if (!response.body) {
    throw new Error('Download response has no body');
  }

  let downloaded = 0;
  const limitStream = new TransformStream<Uint8Array, Uint8Array>({

View on GitHub (pinned to 435076e789)

Solutions

  1. curl -I the URL to see the actual status and confirm it's reachable
  2. Fix the URL (correct tag/release/branch) or make the repo/asset public
  3. For private GitHub resources, export GITHUB_TOKEN or GH_TOKEN before running
  4. If rate-limited (403/429), wait or supply a token; retry with backoff in scripts

Example fix

# before
skills add https://github.com/acme/skills/archive/refs/heads/main.zip  # HTTP 404

# after
curl -I https://github.com/acme/skills/archive/refs/heads/main.zip  # check
skills add acme/skills   # install from git source instead
Defensive patterns

Strategy: retry

Validate before calling

// Probe the URL before downloading
async function assertDownloadable(url: string): Promise<void> {
  const res = await fetch(url, { method: 'HEAD' });
  if (!res.ok) throw new Error(`URL not downloadable: HTTP ${res.status}`);
}

Type guard

null

Try / catch

for (let attempt = 1; attempt <= 3; attempt++) {
  try {
    return await downloadSource(url);
  } catch (err) {
    const msg = err instanceof Error ? err.message : '';
    if (!/^Download failed with HTTP \d+$/.test(msg) || attempt === 3) throw err;
    await new Promise(r => setTimeout(r, attempt * 1000));
  }
}

Prevention

When it happens

Trigger: Calling downloadSource with a URL that returns a non-2xx status: deleted GitHub release asset (404), private repo without a token (403/401), rate-limited API (403/429), or an expired pre-signed URL.

Common situations: Typos or stale URLs pointing to moved/renamed releases, GitHub rate limits hit in CI, private repositories downloaded without GITHUB_TOKEN/GH_TOKEN set, or link rot in documentation.

Related errors


AI-assisted analysis of vercel-labs/skills@435076e789 (2026-08-28). Data as JSON: /api/errors/245a320be4124b47. Report an issue: GitHub.