vercel/turborepo · warning · Error
`git ${args[0]}` failed: ${formatError(error)}
Error message
`git ${args[0]}` failed:
${formatError(error)} What it means
runGit() executes git via execFileSync (clone --filter=blob:none, sparse-checkout, checkout) and wraps any failure — non-zero exit, git missing from PATH (ENOENT), or a killed process — as `git <subcommand> failed:` plus stderr or the error message. downloadAndExtractExample catches this and falls back to tarball download, so the message usually appears inside the 'Git download failed' warning rather than escaping to the caller.
Source
Thrown at packages/turbo-utils/src/examples.ts:517
function formatError(error: unknown): string {
if (error && typeof error === "object" && "stderr" in error) {
const { stderr } = error as { stderr?: unknown };
if (typeof stderr === "string" || Buffer.isBuffer(stderr)) {
const output = stderr.toString().trim();
if (output) {
return output;
}
}
}
return error instanceof Error ? error.message : String(error);
}
function runGit(args: Array<string>, cwd?: string): void {
try {
execFileSync("git", args, { cwd, stdio: "pipe" });
} catch (error) {
throw new Error(`\`git ${args[0]}\` failed:\n${formatError(error)}`);
}
}
function cleanupCloneDirectory(tempDir: string): void {
try {
rmSync(tempDir, {
recursive: true,
force: true,
maxRetries: 5,
retryDelay: 100
});
} catch (error) {
warn(
`Unable to remove temporary directory ${tempDir}:\n${formatError(error)}`
);
}
}
View on GitHub (pinned to 9f94a7d215)
Solutions
- Install git and confirm `git --version` works in the same shell/PATH
- Remove a stale <root>/.turbo-clone-temp directory left by an earlier failed run
- Read the embedded stderr — it is git's real complaint (auth, DNS, index.lock)
- If git is intentionally unavailable, rely on the automatic tarball fallback; only its failure is fatal
Example fix
# before: git missing in minimal container FROM node:22-slim # downloadAndExtractExample warns 'Git download failed' and falls back # after RUN apt-get update && apt-get install -y git
Defensive patterns
Strategy: try-catch
Validate before calling
import { execFileSync } from "node:child_process";
function gitAvailable(): boolean {
try {
execFileSync("git", ["--version"], { stdio: "pipe" });
return true;
} catch {
return false;
}
} Type guard
function isGitFailure(e: unknown): boolean {
return e instanceof Error && e.message.startsWith("`git ") && e.message.includes("` failed:");
} Try / catch
// downloadAndExtractExample already falls back to tarball; wrap the outer call for the rare
// case where BOTH git and the fallback fail:
try {
await downloadAndExtractExample(root, name);
} catch (e) {
if (isGitFailure(e) || e.message.startsWith("Failed to download:")) {
// report stderr embedded in the message; suggest installing git / checking network
}
} Prevention
- Install git in Docker/CI images that run repo scaffolding
- Clean up stale .turbo-clone-temp directories before re-running
- Read the embedded stderr — it distinguishes ENOENT, network, and checkout failures
When it happens
Trigger: git not installed (spawn ENOENT); clone failing due to network/DNS restrictions; sparse-checkout failing because examples/<name> no longer exists upstream; checkout failing on a stale or locked .turbo-clone-temp directory.
Common situations: Slim Docker images and CI runners without git; firewalled environments that allow codeload HTTPS but block git; a leftover .turbo-clone-temp from a previously killed run.
Related errors
- Directory path contains potentially unsafe characters: ${dir
- isErrorLike(reason) ? reason.message : String(reason)
- Failed to download: ${response.status}
- Invalid ${description}: path must be an absolute, non-empty
AI-assisted analysis of vercel/turborepo@9f94a7d215 (2026-08-16).
Data as JSON: /api/errors/ce3fb6a90815d744.
Report an issue: GitHub.