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
- 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
- Check connectivity/proxy: HTTPS_PROXY/HTTP_PROXY env vars must allow codeload.github.com
- If rate limited, wait a few minutes or download the tarball manually and use a local path/template instead
- Retry the create-turbo command; transient GitHub failures are common
- 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
- Run create-turbo from networks with direct access to codeload.github.com (check HTTPS_PROXY)
- In CI, cache or vendor template tarballs instead of fetching per-run to avoid rate limits
- Pre-validate custom repo URLs with a HEAD request before invoking the tool
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
- `git ` failed
- isErrorLike(reason) ? reason.message : String(reason)
- Git download failed: Falling back to downloading the…
- Invalid example name
- --skip-transforms conflicts with <package-manager>. The…
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)