[BUG] SDK sessions escape `cwd` and modify parent repository when running in git worktrees

Resolved 💬 2 comments Opened Apr 1, 2026 by dansw Closed May 8, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

When using the Claude Code SDK with cwd set to a git worktree directory, the CLI session escapes the working directory by running cd commands in Bash, navigating to the parent repository, and executing git operations there (git checkout -b, file edits). This defeats cwd as an isolation boundary and causes data loss in multi-session workflows.

In a real-world incident, an SDK session was given cwd pointing to a worktree at .worktrees/{session-id}/. The worktree was missing some untracked files, so the CLI:

  1. Used EnterWorktree to create its own .claude/worktrees/ worktree
  2. Called ExitWorktree
  3. Navigated to the main repository via cd and absolute paths
  4. Ran git checkout -b feature/new-branch in the main repo
  5. Made all code edits directly in the main repo
  6. Left the main repo on the wrong branch with uncommitted changes

Terminal output from the session confirms the escape:

── Bash ──
$ ls ".worktrees/{session-id}/src/routes/" 2>/dev/null | head -20
[empty - worktree had no files]

── Bash ──
$ git checkout -b feature/new-branch
[executed in main repo, not worktree]

This caused a separate interactive Claude Code session in the same repo to unknowingly be on the wrong branch — subsequent work was applied to the wrong place.

What Should Happen?

When cwd is set via the SDK, the CLI should not write to or execute git commands in directories outside the cwd tree. Proposed options (any one would fix this):

  1. Auto-enable --sandbox when cwd is set programmatically via SDK — macOS Seatbelt / Linux bubblewrap already exist and enforce this at the OS level.

The --sandbox flag already works; it just needs to be the default for SDK usage where cwd implies an isolation boundary.

  1. The CLI detects when Bash commands cd outside the cwd and blocks/warns.
  2. When cwd points to a git worktree, the CLI recognizes the worktree boundary and refuses to operate on the parent repo.

Error Messages/Logs

No error is surfaced. The session silently escapes the cwd and completes "successfully" — the damage is only discovered later when the main repo is found on the wrong branch with uncommitted changes from the escaped session.

Steps to Reproduce

  1. Initialize a git repo with some source files:

```bash
mkdir my-project && cd my-project && git init
echo 'fn main() {}' > src/main.rs
git add -A && git commit -m "init"

  1. Create a worktree for an isolated session:

git worktree add .worktrees/session-1 -b feature-branch

  1. Start a Claude Code session via the SDK with cwd set to the worktree:

from claude_code_sdk import query, ClaudeCodeOptions
result = await query(
prompt="Find and modify the main.rs file",
options=ClaudeCodeOptions(cwd=".worktrees/session-1")
)

  1. If the worktree is missing files the session expects (e.g., untracked files, files in directories not committed), the CLI navigates out via cd

/absolute/path/to/my-project and operates on the main repo.

  1. After the session completes, check the main repo:

cd my-project
git branch --show-current # Will show the feature branch, not main
git status # May show uncommitted changes from the escaped session

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.89 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

  ## How this differs from existing issues

  - **Python SDK issue #10** (`cwd` not honored): Reports `cwd` is ignored entirely — the subprocess starts in the wrong directory. This issue is different: `cwd`
   IS set correctly as the initial working directory, but the CLI **navigates out** during execution via Bash `cd` commands. The subprocess starts right but
  doesn't stay contained.
                                                                                                                                                                  
  - **Python SDK issue #457** (`sandbox_path` feature request): Proposes a new API. This issue shows a concrete incident demonstrating why containment is
  critical, and proposes using the existing `--sandbox` flag rather than a new API surface.                                                                       
                                                                
  ## Workarounds we implemented                                                                                                                                   
                                          
  1. Pass `--sandbox` with `{ "enabled": true, "autoAllowBashIfSandboxed": true, "allowUnsandboxedCommands": false }` to all worktree sessions                    
  2. Post-session detection that checks if main repo HEAD changed and auto-restores the base branch                                                               
  3. Stash any uncommitted changes left by escaped sessions

View original on GitHub ↗

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