vercel/turborepo · error · Error

Failed to download

Error message

Failed to download: ${response.status}

What it means

Thrown by streamingExtract in @turbo-utils/examples.ts when the HTTP response for a tarball download is not ok (non-2xx status) or has no body. This is the streaming path create-turbo uses to fetch the examples tarball (or a custom repo tarball URL). The status code is embedded in the message so you can tell a 404 (wrong path/branch) from a 403/429 (rate limit).

Solutions

  1. Decode the message for the HTTP status: 404 means wrong example name/repo/branch, 403/429 means rate limited (wait or use a different network/token), 5xx means retry later
  2. Check connectivity/proxy: HTTPS_PROXY/HTTP_PROXY env vars must allow codeload.github.com
  3. If rate limited, wait a few minutes or download the tarball manually and use a local path/template instead
  4. Retry the create-turbo command; transient GitHub failures are common
  5. If offline, use an existing local template directory instead of fetching
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check the tarball URL before calling createProject
async function tarballReachable(url: string): Promise<boolean> {
  const res = await fetch(url, { method: "HEAD" });
  return res.ok;
}
// const ok = await tarballReachable("https://codeload.github.com/vercel/turborepo/tar.gz/canary");

Try / catch

try {
  await createProject(opts);
} catch (err) {
  const status = Number(/Failed to download: (\d+)/.exec(String(err?.message))?.[1]);
  if (status === 429 || status >= 500) {
    await sleep(backoffMs); // exponential backoff, then retry once or twice
  } else if (status === 404) {
    throw new Error("Example or branch not found - check the name/URL", { cause: err });
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling createProject()/streamingExtract() where the fetch to the tarball URL (e.g. https://codeload.github.com/vercel/turborepo/tar.gz/canary or a user-supplied repo URL) returns non-2xx: 404 for a nonexistent example/repo/branch, 403 or 429 for GitHub rate limiting, 407/5xx behind a corporate proxy, or a response with a null body.

Common situations: Running create-turbo behind a proxy or firewall that rewrites responses, hitting GitHub's anonymous API/tarball rate limit from CI shared IPs, typo in --example name or a custom repo URL pointing at a nonexistent branch, or being fully offline.

Related errors


AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17). Data as JSON: /api/errors/e05a70d80838298b. Report an issue: GitHub.

Appendix: source

Thrown at packages/turbo-utils/src/examples.ts:346

    controller.abort();
  }, DOWNLOAD_TIMEOUT);

  // Track all write streams so we can clean them up on abort/error
  const writeStreams: Array<Writable> = [];

  // Pre-resolve root once for performance (avoids calling resolve() per entry)
  const resolvedRoot = resolve(root);

  // Cache created directories to avoid redundant mkdirSync calls
  const createdDirs = new Set<string>();

  try {
    const response = await fetch(url, {
      ...buildFetchInit(url),
      signal: controller.signal
    });
    if (!response.ok || !response.body) {
      throw new Error(`Failed to download: ${response.status}`);
    }

    const body = Readable.fromWeb(response.body as ReadableStream);
    let rootPath: string | null = null;

    // Track all file write operations so we can wait for them to complete
    const fileWritePromises: Array<Promise<void>> = [];

    const parser = new Parser({
      filter: (p: string) => {
        // Determine the unpacked root path dynamically instead of hardcoding.
        // This avoids issues when the repository has been renamed.
        if (rootPath === null) {
          const pathSegments = p.split("/");
          rootPath = pathSegments.length ? pathSegments[0] : null;
        }
        return filter(p, rootPath);
      },

View on GitHub (pinned to f9245100cf)