Session Freeze After File Edit - Lock Contention Deadlock

Resolved 💬 5 comments Opened Jan 19, 2026 by tomerbr1 Closed Feb 28, 2026

Summary

Claude Code session becomes completely unresponsive after completing a file edit operation. The freeze occurs during the transition from "PostToolUse hooks completed" back to idle state, when a file system change notification arrives simultaneously.

Environment

  • Claude Code Version: 2.1.12
  • OS: macOS 15.7.3 (Darwin 24.6.0)
  • Architecture: ARM64 (Apple Silicon)
  • Shell: zsh
  • Terminal: iTerm2

Steps to Reproduce

  1. Start a Claude Code session with multiple MCP servers configured
  2. Have PostToolUse hooks configured (e.g., formatter hooks)
  3. Perform multiple file edit operations in sequence
  4. After the final edit completes and hooks finish processing, the session freezes

The freeze appears to be timing-dependent and may require:

  • Long-running session (6+ hours in this case)
  • Multiple concurrent MCP servers
  • File edits that trigger PostToolUse hooks

Expected Behavior

Session should return to idle state and accept user input after completing edit operations and hooks.

Actual Behavior

Session freezes completely:

  • No response to keyboard input
  • Cannot cancel with Ctrl+C
  • CPU usage drops to 0%
  • Only solution is to kill the terminal tab

Last visible output before freeze:

⎿  Running PostToolUse hooks… (1/2 done)

⏺ Done! I've added: [...]

Diagnostic Information

Process State

PID: 31755 | TTY: ttys005 | State: S+ (Sleeping) | CPU: 0.0% | Elapsed: 6h 34m

Thread Analysis (via sample command)

Main Thread - blocked on lock:

872 Thread_1764110   DispatchQueue_1: com.apple.main-thread  (serial)
    + 872 _os_unfair_lock_lock_slow  (in libsystem_platform.dylib)
    +   872 __ulock_wait2  (in libsystem_kernel.dylib)

File Watcher Thread - ALSO blocked on same lock type:

872 Thread_1764227: File Watcher
    + 872 _os_unfair_lock_lock_slow  (in libsystem_platform.dylib)
    +   872 __ulock_wait2  (in libsystem_kernel.dylib)

All Bun Pool Threads (0-11) - waiting:

872 Thread_1764190: Bun Pool 0
    + 872 __ulock_wait2  (in libsystem_kernel.dylib)

HTTP Client Thread - normal (waiting for events):

872 Thread_1764214: HTTP Client
    + 872 kevent64  (in libsystem_kernel.dylib)

Tokio Runtime Workers - normal (parked):

872 Thread_1968467: tokio-runtime-worker
    + 872 _pthread_cond_wait -> __psynch_cvwait

Root Cause Analysis

This appears to be a lock inversion deadlock between:

  1. Main Thread: Trying to acquire a lock while transitioning back to input-waiting state after hooks complete
  2. File Watcher Thread: Trying to acquire the same lock to notify about the file change (the file that was just edited)

The race condition window:

Edit completes → Hooks run → Hooks complete → [RACE WINDOW] → Return to idle
                                                    ↑
                              File Watcher detects change and tries to acquire lock

Configuration

PostToolUse Hooks (2 total):

  1. ruff format $CLAUDE_FILE_PATH 2>/dev/null || black $CLAUDE_FILE_PATH 2>/dev/null || true
  2. Hookify plugin PostToolUse hook (10s timeout)

MCP Servers Running:

  • Playwright MCP
  • Context7 MCP
  • Sequential Thinking MCP
  • Serena MCP (with language servers)
  • GitHub MCP

Child Processes (all healthy)

All MCP servers showed normal kevent wait state - they were not the cause:

PID 32002 (Playwright):  S+ state, waiting on kevent
PID 31988 (Context7):    S+ state, waiting on kevent
PID 32049 (Seq Think):   S+ state, waiting on kevent
PID 32378 (Serena):      S+ state, waiting on kevent
PID 32156 (GitHub):      S+ state, waiting on kevent

Impact

  • Complete session freeze requiring terminal tab closure
  • Loss of unsaved conversation context
  • MCP server processes orphaned (need manual cleanup)

Workaround

None found. Once frozen:

  • kill -CONT does not help (it's a lock contention, not a signal issue)
  • Only option is kill <pid> or closing the terminal

Suggested Investigation Areas

  1. Lock ordering between main event loop and File Watcher in Bun runtime
  2. Race condition during state transition from "processing hooks" to "waiting for input"
  3. File system notification handling during hook completion
  4. Potential fix: Defer file change processing until main thread is fully idle

Additional Files

Full sample output available upon request.

View original on GitHub ↗

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