Background sessions block `Edit`/`Write` outside a worktree with no opt-out — gate is unconditional once `CLAUDE_JOB_DIR` is set

Resolved 💬 2 comments Opened May 12, 2026 by arminta7 Closed May 14, 2026

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)

  1. In a git repo, launch a background Claude Code session (anything that sets CLAUDE_JOB_DIR in the agent's env).
  2. From that session, call the Edit tool on any tracked file at the repo root — do not call EnterWorktree first.
  3. 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:

  • Read any file (no gate)
  • Run arbitrary Bash against the same paths, including in-place rewrites via python3 / 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

  1. Set CLAUDE_NO_AUTO_WORKTREE=1 in ~/.claude/settings.json under env. No effect on this gate. Verified the var is in the daemon's env (env | grep CLAUDE_NO_AUTO_WORKTREE returns =1) but the gate still fires.
  1. Inspected the shipped binary (~/.local/share/claude/versions/2.1.139):
  • strings <binary> | grep CLAUDE_NO_AUTO_WORKTREE returns 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 effectively process.env.CLAUDE_JOB_DIR != null.
  1. CLAUDE_BG_ISOLATION exists in the binary but is set by Agent({ 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_DIR set)
  • Repo: Obsidian vault (Markdown only, no build step), main owned by Obsidian Git

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗