Git 'detected dubious ownership' Error: A 5-Minute Fix Guide by Scenario
Why did git, which worked fine yesterday, suddenly stop?
Your build was working fine yesterday, but today a single git status throws a red error.
fatal: detected dubious ownership in repository at '/workspace'
To add an exception for this directory, call:
git config --global --add safe.directory /workspaceIf you just rebuilt a Docker image, upgraded a CI runner, or installed a newer Git on a shared server, you will almost certainly hit this message. First, don't panic. This is not a code problem, and your repository is not corrupted. Git simply stopped for safety because the directory owner and the user running the command don't match.
This error exploded after the security patch (CVE-2022-24765) was backported to Git 2.35.2 / 2.30.3 and similar versions. Container-based CI/CD became the norm, GitHub Actions self-hosted and container jobs multiplied, and WSL2, NFS shared workspaces, and monorepos became everyday tools — so UID mismatches turned into a normal landscape. If you're in a hurry, copy-paste from the 5-minute fixes by scenario below and read the why later.
Why this error happens: owner ≠ running user
Git compares the owner UID of the repository directory with the UID of the current user running the command. If they differ, Git suspects that someone else may have planted a malicious .git/config (for example, using core.fsmonitor to run arbitrary commands) and refuses to proceed. That is the core of CVE-2022-24765. It is an intentional defense against a scenario where, on a shared path like /tmp that anyone can write to, an attacker pre-creates a Git config that then runs with the victim's privileges. In other words, this is a feature, not a bug. What we need to do is either explicitly tell Git "I trust this directory" or make the owner match the running user in the first place.
5-Minute Fixes by Scenario (Copy-Paste Commands)
① Local / general environment
The most common case. Add a single path to the trust list. Replace <path> with the actual absolute path.
# <path> path replace (: /home/dev/myrepo)
git config --global --add safe.directory <path>This command adds the following entry to ~/.gitconfig. You can open the file to verify or write it by hand.
[safe]
directory = /home/dev/myrepo② Docker container builds
You use Git inside a container, but the owner of the build context or a mounted volume is the host UID, which doesn't match the container's running user. The cleanest fix is to add one line in the Dockerfile build stage.
# path (/workspace path replace)
RUN git config --global --add safe.directory /workspace
# root User , User run
# root when③ Repos cloned with sudo / by another user
You ran sudo git clone, or you're working on a repo that was cloned under a different account. In this case, fixing ownership is the real solution, not just adding a trust exception.
# <path> path replace. User
sudo chown -R $(whoami):$(whoami) <path>④ CI/CD (Jenkins, GitHub Actions, GitLab) and NFS / WSL
This often blows up in CI runner containers, self-hosted runners, NFS mounts, and WSL2 Windows mount paths (/mnt/c/...). Add one step near the start of the job.
# GitHub Actions example — checkout after
- name: Mark workspace as safe
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"# Jenkins / GitLab or entrypoint
git config --global --add safe.directory "$CI_PROJECT_DIR" # GitLab
git config --global --add safe.directory "$WORKSPACE" # JenkinsNote: GitHub Actions'
actions/checkouthandles this setting automatically on the default runner. If it still shows up on container jobs or self-hosted runners, the config created by checkout doesn't match the user actually running Git — add the step above explicitly.
safe.directory '*' — convenient but a dangerous master key
If you're thinking "I don't want to register every path — can't I do it all at once?", you'll find this command.
#
git config --global --add safe.directory '*'⚠️ Security warning
safe.directory '*'turns off owner checks for every directory. That is effectively the same as disabling the CVE-2022-24765 defense. Never overuse this on shared servers, multi-user build servers, or production environments. You are reopening a path for someone else's planted.git/configto run under your privileges.
That said, keep a balanced view. In an isolated, single-user, throwaway CI container with no external attack path and a short lifetime, '*' can be a reasonable choice. The key question is: "Can anyone else's hands reach this environment besides mine?"
In practice, the most common accident is someone baking '*' into a shared build server and forgetting about it. Everyone is comfortable for a while, then the moment that server builds untrusted PR code, the defense is gone. That's why I set the team standard to "register paths individually + bake it into the Docker image," and put '*' on the PR review checklist as allowed only in throwaway containers.
Wrap-up: diagnosis table + FAQ + the real fix
A table so you can go straight from symptom to fix.
| Error / symptom | Root cause | Recommended fix |
|---|---|---|
dubious ownership inside a Docker container | Mounted volume owner (host UID) ≠ container running user | Add RUN git config --global --add safe.directory <path> to the Dockerfile |
Happens on a normal account after sudo git clone | Directory owner is root | sudo chown -R $(whoami):$(whoami) <path> (the real fix) |
| Happens every time on a CI runner (container job / self-hosted) | Workspace owner / running user mismatch per job | After checkout, add a safe.directory "$WORKSPACE" step |
| Happens on NFS / WSL mount paths | Owner mapping on the network / Windows filesystem doesn't match Git's UID | Register that absolute path in safe.directory |
FAQ
Q. Can't I just run as root and be done?
A. It often works — root frequently bypasses the owner check. But I don't recommend it. Privilege escalation maximizes the blast radius if a malicious .git/config runs, and it also makes build artifacts owned by root, which creates more permission problems. It's a workaround that hides the cause (owner mismatch).
Q. I clearly fixed it, but it keeps coming back. Why?
A. Check three things. ① You set --global, but the HOME of the user running Git changes every time (for example, in CI HOME is empty and becomes /), so ~/.gitconfig is never read. ② You need --system to apply it for all users, but you only registered --global. ③ The path is not an absolute path, or a symlink / mount path changes every time. In CI, pin HOME or consider --system.
Q. How do I apply this permanently in CI?
A. Two paths. (1) Bake it into the image — when building the CI base Docker image, add RUN git config --system --add safe.directory <path> so every job inherits it. (2) Add a step in every job — right after checkout, add a safe.directory step. If you control the image, (1) is cleaner; if the workspace path changes per job, (2) is safer.
One-line action guide to close: the temporary fix is safe.directory; the real fix is aligning ownership (chown / matching UIDs). When you're in a hurry, add the path to the trust list and get back to work in 5 minutes. When you have time, match UIDs on containers and mounts so the error never comes back.
Sources and checks vary by article. Only articles with a recorded check date, scope and sources below were checked, and only within that scope. For other articles, verify against the current docs for your version before applying anything, and email us about errors. Editorial policy · email us
This article has no recorded human check (date and scope) yet.
Comments
Be the first to comment.