ErrLookupBackground articles › "git command failed": what it means when a tool shells out to git and git exits non-zero

"git command failed": what it means when a tool shells out to git and git exits non-zero

"git command failed" errors appear when a library, CLI, or build tool runs a git subprocess (status, diff, rev-parse, fetch, worktree, merge-base, and so on) and git exits with a non-zero status. The wrapper message itself is rarely the diagnosis — the real cause is git's stderr or the repository state: not a git repo, unborn HEAD, a missing ref, an index.lock file, dubious ownership, authentication failures, or a corrupt object database.

Distilled from 101 documented records across 21 repositories.

Background

This family sits at the boundary between application code and the git binary. Instead of linking a git library, many tools — turbo, deno, mise, dagger, zed, windmill, grok-build, oh-my-pi, and others — spawn `git` as a child process and treat any non-zero exit as an error. The application's message is a wrapper: what actually failed is the git subcommand inside it. That's why these errors look generic (`git {} failed`, `git command failed`, `git failed with status {status}`) while naming wildly different root causes.

How much detail the wrapper carries is library-specific. Some records embed git's own stderr directly — deno's `git {} failed: {}`, mise's `git -C {} {} failed: {}`, windmill's `git ... failed (exit N): <stderr>`, and the `%w: %s` Go wrappers in dagger and open-code-review all append git's diagnostic, so the message is usually self-explanatory. Others deliberately omit it: nikivdev/code captures stdout instead of inheriting it and bails with a bare `git <args> failed`, mise's streaming path prints git's output to your terminal before bailing with only the exit status, and grok-build falls back to a generic "git command failed" when git exits non-zero with empty stderr. A few wrap richer structures: oh-my-pi raises a GitCommandError carrying the command, return code, and redacted stdout/stderr; zed includes the process status plus collected stderr.

From the caller's side, the error means the tool asked git to do something and git refused — not that the tool itself is broken. The same wrapper surfaces for many distinct repository states: running in a directory that isn't a repo, an unborn branch with no commits, a ref that doesn't exist (or a main-vs-master rename), a stale `.git/index.lock` left by a crashed git process, `safe.directory` dubious-ownership refusals, missing user identity for commit/config operations, authentication or network failures on fetch/push, and genuinely corrupt object databases or indexes. Environment problems also land here: git missing from the PATH (especially for GUI apps and services that inherit a different PATH than your shell), sandboxed environments that forbid spawning subprocesses, timeouts killing long git operations, and read-only or full disks.

The family also varies in consequence. Some tools degrade gracefully — turbo's dirty-hash path warns and falls back to coarser cache keys built only from `git status` filenames, and zed treats unborn-HEAD log failures as empty history. Others hard-fail by design: beads refuses to continue after a worktree create unless `git status` proves the checkout clean, and oh-my-pi deliberately raises on a failed `git worktree prune` (including the 124 timeout code) so the cleanup event retries instead of recording success while stale metadata blocks the next worktree add. Edge cases worth knowing: git uses exit code 1 as a legitimate "no" answer for `merge-base --is-ancestor`, so zed treats only codes above 1 as infrastructure failure; and jj (Jujutsu) has a parallel failure path in grok-build with the same empty-stderr fallback shape.

Common causes

What usually fixes it

Go deeper

Documented occurrences

…and 81 more across the corpus — use search.

Honest provenance: generated on 2026-09-05 from AI-assisted analysis of the linked records. See how records are made.