WorktreeCreate/WorktreeRemove hooks: fragile stdout handling causes silent hang

Resolved 💬 3 comments Opened Feb 21, 2026 by tfriedel Closed Mar 22, 2026

Summary

WorktreeCreate hooks silently hang when the hook command produces any extra stdout beyond the expected worktree path. There's no error, no timeout — Claude just freezes and requires Ctrl-C to exit.

Environment

  • Claude Code v2.1.50
  • Linux (WSL2)
  • Git worktree as the VCS backend

Problem

1. Extra stdout causes a silent hang (main issue)

git worktree add prints status info to stdout (e.g., HEAD is now at 1424fd1 ...). Claude Code concatenates this with the echoed worktree path, can't parse it, and hangs indefinitely with no error message.

Debug log shows:

WorktreeCreate [...] completed with status 0
Hook output does not start with {, treating as plain text
Created hook-based worktree at: HEAD is now at 1424fd1 Add WorktreeCreate hook and dummy script /home/user/projects/dummy/.claude/worktrees/wt-test92

The hook exited successfully but Claude becomes unresponsive. The user must Ctrl-C to recover.

The documentation does mention that the hook must print the absolute path to stdout, and the SVN example correctly redirects VCS output to stderr (>&2). However, the failure mode when this isn't done correctly is very poor — a silent hang with no indication of what went wrong.

Workaround: Redirect git output to stderr: git worktree add ... >&2

2. Inline commands don't reliably receive stdin

Using a command string directly in settings.json:

{ "type": "command", "command": "cat /dev/stdin > /tmp/debug.json" }

Results in an empty file — stdin is not delivered. Switching to a script file fixes this. Likely caused by shell profile sourcing consuming stdin before the command runs.

Expected behavior

  1. If the hook exits 0 but stdout contains unexpected content (e.g. multiple lines, not a valid path), Claude should show an error message rather than hanging silently. Something like: "WorktreeCreate hook produced unexpected output. Expected a single line with the absolute worktree path, got: ..."
  2. A timeout or error recovery mechanism should prevent indefinite hangs
  3. Inline commands should reliably receive stdin, same as script files

Working configuration (for reference)

settings.json:

{
  "hooks": {
    "WorktreeCreate": [
      { "hooks": [{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/scripts/worktree-create.sh" }] }
    ],
    "WorktreeRemove": [
      { "hooks": [{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/scripts/worktree-remove.sh" }] }
    ]
  }
}

scripts/worktree-create.sh:

#!/usr/bin/env bash
set -euo pipefail
INPUT=$(cat)
NAME=$(echo "$INPUT" | jq -r '.name')
WORKTREE_PATH="$CLAUDE_PROJECT_DIR/.claude/worktrees/$NAME"
git worktree add "$WORKTREE_PATH" -b "$NAME" >/dev/null 2>&1
echo "$WORKTREE_PATH"

scripts/worktree-remove.sh:

#!/usr/bin/env bash
set -euo pipefail
INPUT=$(cat)
WORKTREE_PATH=$(echo "$INPUT" | jq -r '.worktree_path')
git worktree remove "$WORKTREE_PATH"

View original on GitHub ↗

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