v2.1.23+ causes 100% CPU hang with multiple parallel instances and hooks

Status Fixed / completed
Reported on v2.1.27
Maintainer reply None cached
Activity 9 comments · opened Jan 31, 2026 · closed Aug 19, 2026

Description

Claude Code hangs with 100% CPU usage when running multiple parallel instances with hooks enabled. This regression was introduced somewhere between v2.1.22 and v2.1.23.

Environment

  • OS: Linux (WSL2, Ubuntu)
  • Claude Code version: 2.1.27 (also affects 2.1.25)
  • Installation: Native client (not npm)
  • Profile: Custom config directory via CLAUDE_CONFIG_DIR

Steps to Reproduce

  1. Enable hooks in settings (disableAllHooks: false)
  2. Have multiple plugins with hooks enabled (e.g., PreToolUse hooks for Bash, Write, Edit)
  3. Start 2+ Claude Code instances in parallel
  4. One or more instances will hang at 100% CPU

Expected Behavior

Multiple parallel instances should work without hanging, as they did in v2.1.22.

Actual Behavior

  • Instances hang with ~100% CPU usage
  • Even simple commands like /plugin cause hangs
  • The UI becomes unresponsive
  • Process must be killed with kill -9

Workaround

Using v2.1.22 resolves the issue completely:

CLAUDE_CONFIG_DIR=$HOME/.claude-work /path/to/versions/2.1.22

With v2.1.22, everything works perfectly - all hooks, all plugins, multiple parallel instances.

Potentially Related Changes

Looking at the changelog, these changes between 2.1.22 and 2.1.27 might be relevant:

  • v2.1.27: "Content-level ask now takes precedence over tool-level allow" - permission handling change
  • v2.1.23: "Fixed race condition causing 400 errors with prompt caching scope" - might have introduced new race conditions
  • v2.1.23: "Fixed per-user temp directory isolation on shared systems"

Hook Configuration

The hooks are standard PreToolUse/PostToolUse hooks that return JSON with permissionDecision. Example hook output:

{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}

Additional Context

  • The issue does NOT occur with v2.1.22 regardless how many sessions in parallel and regardless which hooks are enabled or disabled
  • CPU usage stays at around 90-100% until the process is manually killed
  • No error messages are displayed before the hang
  • The issue does occur on the first user prompt then it hangs completely

View original on GitHub ↗

8 Comments

github-actions[bot] · 7 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/22057
  2. https://github.com/anthropics/claude-code/issues/22149
  3. https://github.com/anthropics/claude-code/issues/22141

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

Marcel-Bich · 7 months ago

Correction: Disabling hooks (disableAllHooks: true) does NOT fix the issue. The only confirmed workaround is downgrading to v2.1.22.

This makes the issue more similar to #22158 - it appears to be a fundamental problem in v2.1.23+ rather than hook-specific.

Denubis · 7 months ago

It's still present in .29, and feels different than the .27 crashes from yesterday. It was hanging on pretooluse for me, but only on certain calls.

edwinhu · 7 months ago

Subscribing - experiencing similar issues

jcfischer · 6 months ago

same problems for me

yurukusa · 5 months ago

I run Claude Code on WSL2 with 8 hooks (via npx cc-safe-setup) and haven't hit this CPU hang — but I typically run 1 instance, not multiple in parallel.

Possible workaround for parallel instances:

Ensure hooks use file locks to avoid concurrent execution conflicts:

#!/bin/bash
# Add at the top of each hook to prevent concurrent execution issues
LOCK="/tmp/claude-hook-$(basename "$0").lock"
exec 200>"$LOCK"
flock -n 200 || exit 0  # Skip if another instance is running this hook

Alternative — reduce hook execution for parallel sessions:

Check if the hook is being called too frequently by logging timestamps:

LAST="/tmp/claude-hook-last-$(basename "$0")"
NOW=$(date +%s)
PREV=$(cat "$LAST" 2>/dev/null || echo 0)
echo "$NOW" > "$LAST"

# Skip if last execution was less than 100ms ago
if [ $((NOW - PREV)) -eq 0 ]; then
    exit 0  # Too frequent, skip
fi

The 100% CPU pattern suggests hooks are being spawned in a tight loop. File locking prevents the cascade.

boshu2 · 4 months ago

Adding macOS Apple Silicon evidence + quantitative scope to this thread, since this issue is framed Linux-WSL2 only but the failure mode appears identical on macOS.

I filed #55609 earlier today for what I now believe is the same bug: 100% CPU hang on hook-configured claude after backgrounded-bash interrupt. The bot suggested this issue as a duplicate, and reading your repro, I agree.

Two corrections vs the framing here:

  1. Single instance is enough. I don't need 2+ parallel claudes to reproduce — single claude session with any hook in ~/.claude/settings.json (SessionStart / SessionEnd / Stop / PreToolUse / PostToolUse) freezes on backgrounded-bash interrupt. ~60 second repro.
  2. Affects macOS Apple Silicon as severely as Linux, possibly more — on macOS the hang escalates to whole-userland stall (kernel rejects new fork() calls system-wide). Not just process-stuck, system-stuck.

Quantitative scope from a freeze-capture monitor I had running this morning (08:32–12:05 PDT, ~3.5 hours active dev):

  • 21 distinct freeze events captured across 5 separate claude PIDs (28428, 42495, 47974, 55383, 99538)
  • System 1-min load average peaked at 18.99 during freezes, baseline 3.37 (5.6× idle)
  • The capture monitor itself hit fork: retry: Resource temporarily unavailable repeatedly while trying to spawn sample/vm_stat — kernel was system-wide out of process slots, not just claude stuck

Stack trace from one stuck claude (PID 77747, 2026-05-02 08:27:44, claude 2.1.126, macOS 26.4.1):

  • 2,561 thread samples on com.apple.main-thread (serial)
  • All 2,561 in same call path: + 0x6569a0 → 0xe37c6c → 0x849910 → ...
  • Same syscall-loop signature as @smconner's Linux x86_64 gcore disassembly in #18532 — V8 JIT property-descriptor-hash-table loop

I haven't downgraded to v2.1.22 yet but will test if it would help diagnosis. Also worth noting: the bot also flagged #52253 ("tree-kill dependency causes pgrep storm / 100% CPU when reaping Bash subprocess trees on macOS") as a duplicate — the tree-kill / pgrep angle in that title matches my stack trace exactly. Could be the same root cause as the regression introduced in 2.1.23.

Forensic dir (7 freeze snapshots, 21-event log, sample profiles for 5 PIDs, vm_stat captures, fork-failure stdout) ready to share. Disclosure: I build AgentOps on top of Claude Code; filing as a daily user.

boshu2 · 4 months ago

Confirming this regression still reproduces on macOS in 2.1.126 (originally filed for Linux/WSL2 — bug is platform-agnostic). 4-step repro in #55609 triggers it deterministically; vanilla settings.json ({}) does not.

System spindump caught the parent main-thread in a posix_spawn loop with kernel turnstile waits naming pgrep children — the tree-kill/Bg6 storm tracked in #52253. Forensic detail: #52253 comment.

Showing cached comments. Read the full discussion on GitHub ↗