vercel/turborepo · warning
failed to get git status for dirty hash
Error message
failed to get git status for dirty hash: {e} What it means
Part of the SCM dirty-hash computation that makes cache keys more conservative for modified workspaces: turbo runs `git status --porcelain -z` and mixes the result into a hash of the working-tree state. If that git command errors, turbo emits this warning and returns None, so the dirty state contributes nothing to the cache key. Caching still works, but it is less precise about local modifications.
Solutions
- Verify git works in the same environment: run `git status --porcelain` in the repo root
- For ownership errors run `git config --global --add safe.directory /path/to/repo`
- Install git in the CI image or add it to PATH
- If .git is corrupted, re-clone the repository
Example fix
# before: dubious ownership error fatal: detected dubious ownership in repository at '/repo' # after git config --global --add safe.directory /repo turbo run build
Defensive patterns
Strategy: validation
Validate before calling
# Confirm git works where turbo will run
git -C "$REPO_ROOT" status --porcelain >/dev/null \
|| { echo 'git status failed — fix before turbo'; exit 1; }
turbo run build Prevention
- Include git in CI images that run turbo
- For containers sharing a host checkout, set `git config --global --add safe.directory <repo>`
- Avoid running turbo against a .git owned by a different user
- Know the consequence: a failed status read removes the dirty-state contribution from cache keys
When it happens
Trigger: `execute_git_command(["status", "--porcelain", "-z"])` fails in the repo root: git not installed or not on PATH, the directory not actually a git repository (or .git corrupted), or git refusing to run due to 'dubious ownership' (safe.directory).
Common situations: Slim CI images without git; a checkout created outside the container while turbo runs inside; files owned by root vs the container user triggering safe.directory; a broken .git after interrupted operations.
Understand the failure class
Background: "git command failed": what it means when a tool shells out to git and git exits non-zero — this error's family across 21 libraries.
Related errors
- failed to run git diff for dirty hash
- --affected could not determine changed files. All tasks…
- [33m"turbo-ignore" is deprecated. Use "turbo query…
- Directory path contains potentially unsafe characters
- error creating log file directory
AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17).
Data as JSON: /api/errors/64baa36efbf5a814.
Report an issue: GitHub.
Appendix: source
Thrown at crates/turborepo-scm/src/git.rs:238
/// Compute a hash summarizing all uncommitted state in the working tree.
/// Uses `git status --porcelain -z` (which files are dirty/untracked) and
/// `git diff HEAD` (the actual content changes for tracked files) as inputs
/// to a SHA-256 hash. Returns `None` if the working tree is clean or if
/// git commands fail (with a warning logged).
///
/// The diff output is streamed through the hasher to avoid buffering
/// arbitrarily large diffs into memory. `--no-ext-diff` and `--no-binary`
/// ensure deterministic, bounded output regardless of user git config.
///
/// Note: content of untracked files (not yet `git add`ed) is not included
/// in the diff — only their filenames from `git status` contribute.
fn get_dirty_hash(&self) -> Option<String> {
use sha2::{Digest, Sha256};
let status_output = match self.execute_git_command(&["status", "--porcelain", "-z"], "") {
Ok(output) => output,
Err(e) => {
turborepo_log::warn(
turborepo_log::Source::turbo(turborepo_log::Subsystem::Scm),
format!("failed to get git status for dirty hash: {e}"),
)
.emit();
return None;
}
};
if status_output.is_empty() {
return None;
}
let mut hasher = Sha256::new();
hasher.update(&status_output);
self.finish_dirty_hash(hasher, true)
}
fn get_dirty_hash_from_repo_index(&self, repo_index: &RepoGitIndex) -> Option<String> {View on GitHub (pinned to f9245100cf)