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

  1. Verify git works in the same environment: run `git status --porcelain` in the repo root
  2. For ownership errors run `git config --global --add safe.directory /path/to/repo`
  3. Install git in the CI image or add it to PATH
  4. 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

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


AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17). Data as JSON: /api/errors/64baa36efbf5a814. Report an issue: GitHub.