[BUG] WorktreeCreate/WorktreeRemove hooks from multiple settings levels cause race conditions
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?
Summary
When WorktreeCreate (or WorktreeRemove) hooks are defined at both the user level (~/.claude/settings.json) and the project level (.claude/settings.json), both hooks execute in parallel. This causes a race condition where the faster (simpler) user-level hook wins, and the project-level hook sees the worktree as already created — silently bypassing the project's custom worktree logic.
Actual Behavior
Both hooks run in parallel. The user-level hook (simpler, faster) creates the worktree first. The project-level hook then detects the branch is already checked out and exits early, returning the path created by the user-level hook — completely bypassing the project's custom logic.
Impact
- Silent failure: No error is shown. The project hook silently falls back to the user-level result.
- Lost functionality: Project-specific setup (environment files, dependency installation, schema configuration) is completely skipped.
- No workaround at project level: The only fix is to remove the user-level hooks entirely, which defeats the purpose of having a generic fallback for other repositories.
What Should Happen?
Expected Behavior
Project-level hooks should either:
- Override user-level hooks for the same event (project-specific config takes precedence), or
- Run sequentially with a defined order (project after user), so the project hook can act on the result, or
- Provide a documented precedence/merge strategy for hooks that produce output (like
WorktreeCreatewhich returns a directory path)
Error Messages/Logs
Silently fails.
Steps to Reproduce
Minimum Reproducible Example
1. User-level settings (~/.claude/settings.json)
{
"hooks": {
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'N=$(jq -r .name); C=$(jq -r .cwd); R=$(git -C \"$C\" rev-parse --show-toplevel); D=\"$R/.worktrees/$N\"; mkdir -p \"$R/.worktrees\"; { git -C \"$C\" worktree add \"$D\" -b \"$N\" || git -C \"$C\" worktree add \"$D\" \"$N\"; } >&2 && echo \"$D\"'"
}
]
}
]
}
}
This creates worktrees at .worktrees/<name> (e.g. .worktrees/my-feature).
2. Project-level settings (.claude/settings.json in repo root)
{
"hooks": {
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'N=$(jq -r .name); R=$(git rev-parse --show-toplevel); cd \"$R\"; ID=$(ls -d .worktrees/[0-9]* 2>/dev/null | sed \"s/.*\\///;s/-.*//' | sort -n | tail -1); ID=$((${ID:-0}+1)); D=\"$R/.worktrees/$ID-$N\"; mkdir -p \"$R/.worktrees\"; git worktree add \"$D\" -b \"$N\" >&2 && echo \"$D\"'"
}
]
}
]
}
}
This creates numbered worktrees at .worktrees/<id>-<name> (e.g. .worktrees/42-my-feature).
3. Reproduction steps
# Initialize test repo
mkdir /tmp/test-repo && cd /tmp/test-repo
git init && git commit --allow-empty -m "init"
mkdir -p .claude
# Create project settings with numbered worktree hook (see above)
cat > .claude/settings.json << 'EOF'
{ "hooks": { "WorktreeCreate": [{ "hooks": [{ "type": "command", "command": "bash -c 'N=$(jq -r .name); echo \"PROJECT HOOK: creating numbered worktree\" >&2; R=$(git rev-parse --show-toplevel); D=\"$R/.worktrees/1-$N\"; mkdir -p \"$R/.worktrees\"; git worktree add \"$D\" -b \"$N\" >&2 && echo \"$D\"'" }] }] } }
EOF
# Ensure user settings also define WorktreeCreate (see above)
# Launch Claude Code and create a worktree
claude -w my-feature
4. Expected result
Worktree created at .worktrees/1-my-feature (numbered, from project hook).
5. Actual result
Worktree created at .worktrees/my-feature (unnumbered, from user hook). The project hook finds the branch already checked out and exits early.
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.81
Platform
Anthropic API
Operating System
Other Linux
Terminal/Shell
Other
Additional Information
Suggested Fix
For hooks that produce output consumed by Claude Code (specifically WorktreeCreate which returns a directory path):
- Option A: Project-level hooks should take precedence over user-level hooks. If a project defines
WorktreeCreate, the user-levelWorktreeCreateshould not run. - Option B: Allow a
priorityorexclusiveflag so project hooks can declare they replace user-level hooks for the same event. - Option C: Run hooks sequentially (project after user) and let the last hook's output win — with the ability for later hooks to see the previous result.
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗