CLAUDE LABJP
PRICING — September 1 was the scheduled date for the Sonnet 5 price increase, and it did not happen. The introductory $2/$10 per MTok now stands as the regular pricePARTNER — Salesforce and Anthropic announced Claudeforce, an expanded partnership. The Salesforce in Claude plugin ships with 37 prebuilt sales skills, from meeting prep to pipeline managementTRUST — Claudeforce serves Claude through Amazon Bedrock inside the Salesforce Trust Boundary, so data and inference never leave the security perimeter — an answer aimed squarely at regulated industriesBETA — Salesforce in Claude is with select pilot customers for now, with an open beta expected during SeptemberLIMITS — The 50% weekly-limit boost runs through September 13. From September 14 the permanent level is 25% above the pre-promotion baseline, roughly a 17% cut from todayRELEASE — Claude Code has shipped nothing since v2.1.251 on August 28. Against a pace of one release every 0.8 days, a four-day gap is among the longest yetPRICING — September 1 was the scheduled date for the Sonnet 5 price increase, and it did not happen. The introductory $2/$10 per MTok now stands as the regular pricePARTNER — Salesforce and Anthropic announced Claudeforce, an expanded partnership. The Salesforce in Claude plugin ships with 37 prebuilt sales skills, from meeting prep to pipeline managementTRUST — Claudeforce serves Claude through Amazon Bedrock inside the Salesforce Trust Boundary, so data and inference never leave the security perimeter — an answer aimed squarely at regulated industriesBETA — Salesforce in Claude is with select pilot customers for now, with an open beta expected during SeptemberLIMITS — The 50% weekly-limit boost runs through September 13. From September 14 the permanent level is 25% above the pre-promotion baseline, roughly a 17% cut from todayRELEASE — Claude Code has shipped nothing since v2.1.251 on August 28. Against a pace of one release every 0.8 days, a four-day gap is among the longest yet
Articles/Claude Code
Claude Code/2026-05-30Intermediate

Why git Says detected dubious ownership in repository — and How to Get Past It

An automation that ran fine yesterday suddenly dies on detected dubious ownership in repository. Here is what actually triggers it, the safe.directory fix, and how it differs from a real Permission denied.

Claude Code243git17safe.directorycontainerstroubleshooting89

One morning, an automation that had been running quietly for weeks stopped on a single line:

fatal: detected dubious ownership in repository at '/tmp/repos/my-app'
To add an exception for this directory, call:

	git config --global --add safe.directory /tmp/repos/my-app

As an indie developer who has shipped apps since 2014, I keep four Dolice sites updated daily through scheduled Claude Code runs, and the moment the sandbox switched to a different user, every git pull against the existing clone failed on this. The message is unusually friendly, yet the first time you see it the real question is: why did something that worked yesterday break today? Let me walk through it in the order I actually hit each piece.

What "dubious ownership" really means

This is not a git bug and it is not a corrupted repository. It fires when the user running git does not own the repository directory, and git deliberately refuses to continue as a safety measure.

The reason traces back to CVE-2022-24765. On a shared machine or a temporary directory like /tmp, if someone else drops a .git/config there and git reads it under your privileges, settings such as core.fsmonitor could be used to run arbitrary commands. So from Git 2.35.2 onward, touching a repository owned by another user stops immediately.

In my case the ownership told the whole story at a glance:

$ ls -ld /tmp/repos/my-app /tmp/repos/my-app/.git
drwxr-xr-x 7 nobody nogroup 4096 /tmp/repos/my-app
drwxr-xr-x 8 nobody nogroup 4096 /tmp/repos/my-app/.git
 
$ id -un
runner

The directory belongs to nobody, but the process is running as runner. When a sandbox's UID changes between one run and the next, this kind of mismatch is completely normal in containers and ephemeral environments.

The fastest fix: register a safe.directory

If you can vouch that the repository is trustworthy, the quickest path is exactly what the message suggests — add it as an exception:

git config --global --add safe.directory /tmp/repos/my-app

That whitelists just this directory, and your subsequent git pull or git status go through. The entry lands in the running user's global ~/.gitconfig under a [safe] section, so you can confirm it like this:

$ git config --global --get-all safe.directory
/tmp/repos/my-app

The detail that matters is --add. Without it, repeating git config --global safe.directory ... overwrites the previous value, which bites you the moment you handle more than one repository. Once I started registering each repo path with --add at the top of my automation scripts, this whole class of stumble disappeared.

Wildcards are handy, but mind the blast radius

If adding a path every time feels tedious, you can trust every repository with a wildcard:

git config --global --add safe.directory '*'

For a throwaway environment — a CI container that you fully control and rebuild on every run — this is pragmatic, and I use * in disposable build containers myself.

But avoid * on a shared development machine, or anywhere someone else's repository might appear. It removes the ownership check entirely and hands back the exact risk CVE-2022-24765 was meant to guard against. Whether the environment is disposable is, I think, the cleanest line to draw.

"dubious ownership" and "Permission denied" are not the same thing

This was the trap I fell into that same morning. After registering safe.directory, the error simply changed:

error: cannot open .git/FETCH_HEAD: Permission denied

It is tempting to read this as "my safe.directory setting did not take," but it is a different problem altogether:

  • detected dubious ownership — git is voluntarily stopping because the owner differs. A config (safe.directory) clears it.
  • Permission denied (when writing FETCH_HEAD and friends) — the OS file permissions genuinely will not let you write. No git config fixes this.

If a runner user tries to git pull into a directory owned by nobody, no amount of safe.directory entries will help — the OS rejects the write. The reliable check is to look at the actual permissions and confirm whether the write bit is yours:

$ touch /tmp/repos/my-app/.git/_wtest && echo OK || echo "NOT WRITABLE"
NOT WRITABLE

Once you know you cannot write, you are no longer in the "tweak a setting" phase. You are in the "fix the ownership, or move to a writable place" phase.

Root fixes that match the environment

To resolve the ownership mismatch permanently, pick whichever fits your situation.

If you have the rights to reclaim ownership, lining it up with the running user via chown is the textbook move:

sudo chown -R "$(id -un):$(id -gn)" /tmp/repos/my-app

If sudo is unavailable, or you are in a shared environment where you must not change ownership, the dependable choice is to rebuild somewhere you can definitely write. Because my scheduled runs periodically found /tmp owned by another user and unwritable, I switched to falling back to a writable spot under $HOME:

WORK="$HOME/repos/my-app"
if [ -d "$WORK/.git" ] && [ -w "$WORK/.git" ]; then
  cd "$WORK" && git pull --rebase origin main
else
  rm -rf "$WORK"
  git clone --depth 1 "$REPO_URL" "$WORK"
fi

Cloning as the same user that will eventually run git helps too. A Docker build that clones as root and then drops to a non-root user at runtime produces exactly this ownership split. Cloning after the USER switch, or slipping in a single chown, prevents it before it starts.

So the same morning does not repeat

At root, this error collapses to one question: who owns the file? Checking ownership and writability up front, before anything runs, is far faster than diagnosing a halt after the fact. I now keep these three lines at the top of my scripts:

git config --global --add safe.directory "$WORK"
[ -w "$WORK/.git" ] || WORK="$HOME/repos/$(basename "$WORK")"
echo "owner=$(stat -c '%U' "$WORK" 2>/dev/null) me=$(id -un)"

Register safe.directory to clear the ownership check, escape to a writable location if you cannot write, and finally log the owner next to yourself. That alone makes it obvious, when you open the log the next morning, what actually happened.

The more often a system runs, the more these quiet broken assumptions add up. I hope this saves someone else the same morning.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Claude Code2026-05-27
When Claude Code's Bash Tool Hits Permission Denied on /tmp — A $HOME/repos Fallback Pattern
A practical look at why git clone inside Claude Code's sandboxed Bash sometimes fails with Permission denied on /tmp, and how a tiny $HOME/repos fallback keeps unattended schedules alive across four indie sites.
Claude Code2026-08-30
Your Config Asks for effort xhigh With Thinking Off, and It Now Quietly Runs at high
Disabling thinking while asking for effort xhigh or max returns a 400. That failure now degrades silently to high instead, so here is a small preflight that catches the mismatch before you send the request.
Claude Code2026-08-25
A One-Letter Typo in settings.json Is Ignored Without a Single Warning
I diffed claude doctor output between a settings.json with misspelled keys and a correct one. There was no difference at all. Here is what actually gets validated, what slips through, and a small check that catches typos before they cost you a day.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →