vercel/turborepo · warning
failed to get git status for dirty hash: {e}
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.
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)
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.
Related errors
- failed to run git diff for dirty hash
- --affected could not determine changed files. All tasks will
- Directory path contains potentially unsafe characters: ${dir
- Invalid ${description}: path must be an absolute, non-empty
- `git ${args[0]}` failed: ${formatError(error)}
AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17).
Data as JSON: /api/errors/64baa36efbf5a814.
Report an issue: GitHub.