Background sessions block `Edit`/`Write` outside a worktree with no opt-out — gate is unconditional once `CLAUDE_JOB_DIR` is set
Summary
When a Claude Code session is launched as a background job (i.e. with CLAUDE_JOB_DIR set in the environment), the Edit and Write tools refuse to operate on any path inside the current checkout unless the session is currently inside .claude/worktrees/.... The refusal message is:
"This background session hasn't isolated its changes yet. Call EnterWorktree first so edits land in a worktree instead of the shared checkout, then retry this edit using the worktree path."
There is no documented or undocumented env var / settings.json key that disables this gate. This is a problem for non-code repos where worktrees are actively harmful — notably Obsidian vaults, where another process (Obsidian.app or Obsidian Git) owns the working copy and would not see edits made on a sibling worktree branch.
Repro (verified on 2.1.139)
- In a git repo, launch a background Claude Code session (anything that sets
CLAUDE_JOB_DIRin the agent's env). - From that session, call the
Edittool on any tracked file at the repo root — do not callEnterWorktreefirst. - The tool returns the error above. Tool output is rejected at the harness/preflight layer; the user-supplied file content never lands.
The same session can:
Readany file (no gate)- Run arbitrary
Bashagainst the same paths, including in-place rewrites viapython3/sed -i ''(no gate) - Run external CLIs that mutate the same files (e.g.
obsidian append file=... content=...) (no gate)
So the gate is specific to the Edit/Write tool path, and trivially circumventable from Bash, which makes its protective value low while the friction is high.
What I tried
- Set
CLAUDE_NO_AUTO_WORKTREE=1in~/.claude/settings.jsonunderenv. No effect on this gate. Verified the var is in the daemon's env (env | grep CLAUDE_NO_AUTO_WORKTREEreturns=1) but the gate still fires.
- Inspected the shipped binary (
~/.local/share/claude/versions/2.1.139):
strings <binary> | grep CLAUDE_NO_AUTO_WORKTREEreturns 0 matches. The env var name doesn't appear anywhere in the binary, so it's a complete no-op. (This deserves either implementation or documentation as not-a-thing — multiple Claude sessions appear to suggest it as a fix.)- The gate string is reachable. Surrounding logic (decompiled from raw bytes):
``js``
// pseudocode
if (currentWorktreeSession) return targetInOriginalCwd && !targetInWorktreePath
? "This session is now isolated in <wt>. Edit the worktree copy of this file instead of the shared-checkout path."
: null;
const cwd = getOriginalCwd();
if (cwd.includes("/.claude/worktrees/")) return null; // already in a wt — fine
if (!target.startsWith(cwd + sep)) return null; // outside repo — fine
if (!isBgSessionRequiringIsolation(cwd)) return null; // bg-session predicate
return "This background session hasn't isolated its changes yet. Call EnterWorktree first…";
isBgSessionRequiringIsolation(cwd)is the only configurable hook in the chain, and there's no string in the binary that suggests it reads any env var or settings key — it's effectivelyprocess.env.CLAUDE_JOB_DIR != null.
CLAUDE_BG_ISOLATIONexists in the binary but is set byAgent({ isolation: "worktree" })per spawn and controls a different preflight message ("Call EnterWorktree as your first action — before reading files or running commands"). It is not a global on/off and setting it to""from outside has no effect on the gate above.
Proposed fix
Add an opt-out — either a settings key or an env var — that makes isBgSessionRequiringIsolation return false. E.g.:
// ~/.claude/settings.json
{
"worktree": {
"requireForBackgroundSessions": false
}
}
Or equivalently CLAUDE_BG_REQUIRE_WORKTREE=0. Either name is fine — the important thing is that there be one.
Documenting CLAUDE_NO_AUTO_WORKTREE as either implemented-and-doing-X or not-a-real-flag would also help; right now it appears in conversations and settings.json as a remedy but does nothing.
Why this matters
Non-code repos are a real use case for Claude Code. The Obsidian-vault pattern (Markdown notes managed by a separate desktop app that holds the canonical state) is the obvious one, but the same applies to any repo where another process — a deployment pipeline, a sync daemon, a doc generator — owns the working copy and worktree branches are friction rather than safety. For these, the background-session bg-worktree gate is pure cost: it adds a merge step, hides changes from the owning app, and is bypassable from Bash anyway.
A clean opt-out would let users keep the default-safe behavior in code repos while disabling it where it isn't wanted.
Environment
- Claude Code
2.1.139(binary path:~/.local/share/claude/versions/2.1.139) - macOS 25.5.0, arm64
- Session type: background job (
CLAUDE_JOB_DIRset) - Repo: Obsidian vault (Markdown only, no build step),
mainowned by Obsidian Git
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗