/resume picker enters sustained high-CPU spin loop (~140%) with alternating selection — binary analysis of 2.1.218

Status Open
Reported on v2.1.218
Maintainer reply None cached
Activity 0 comments · opened Jul 23, 2026

Summary

The /resume session picker enters a sustained spin loop — 140–153% CPU, ~800 MB V8 heap — while the highlighted selection alternates between two adjacent rows at ~300 ms intervals. Reproduces on Windows 11 Enterprise 10.0.26200.

Environment

  • Version: 2.1.218
  • OS: Windows 11 Enterprise 10.0.26200 (Claude Code in PowerShell)

Steps to reproduce

  1. Type /resume inside an active Claude Code session.
  2. Leave the picker open with ≥1 session in the list.
  3. Observe process CPU — does not drop to ~0% after the list renders.

Root cause (binary analysis of claude.exe)

Three defects interact to produce the loop:

Bug 1 — Date.now() as React key (offsets ~254,384,200 and ~252,666,300):

return ZW.jsx(jgS, {onDone: e, onResume: n}, Date.now());

A changed key forces full unmount → remount. Currently the handler is called once per invocation so the key is stable — but any re-invocation triggers an immediate remount.

Bug 2 — useEffect([y]) where y = useCallback(..., [onDone]) (jgS / NsS, offsets ~254,382,709 and ~252,663,383):

let y = cce.useCallback(async (b, T) => {
    let C = b ? await J8s() : await tbr(T);
    n(C);  // setSessions
}, [e]);   // e = onDone

cce.useEffect(() => {
    async function b() { let T = await Bxe(gn()); y(false, T); }
    b();
}, [y]);   // fires whenever onDone reference changes

If onDone gets a new reference (e.g. on remount from Bug 1), y changes, the effect fires, sessions reload from disk, state updates, re-render — loop.

mjo bidirectional focus circuit (offset ~250,774,988):

K4.useEffect(() => { if (A !== void 0) s.current?.(A) }, [A]);      // internal → onFocus
K4.useEffect(() => { if (n !== void 0) i({type:"set-focus", value:n}) }, [n]);  // prop → reducer

A session reload produces a new array reference → mjo resets focus state → onFocus fires → parent updates focusedValue → prop bounces back. The set-focus guard terminates single-shot loops but not loops driven by repeated array reloads.

The ~300 ms interval is the round-trip latency of the async disk scan in Bxe(gn()) plus React reconciler commit time — not a timer. Ink's drain timer is Gj >> 2 = 16 >> 2 = 4 ms (confirmed at offset 243,187,215) and is unrelated.

Fix

// Bug 1: drop the Date.now() key
return ZW.jsx(jgS, {onDone: e, onResume: n});

// Bug 2: gate the reload to fire once
cce.useEffect(() => {
    if (!hasFetched.current) {
        hasFetched.current = true;
        async function b() { let T = await Bxe(gn()); y(false, T); }
        b();
    }
}, [y]);

Note: vQS = ResumeConversation (the claude --resume startup picker) does not have these bugs — it uses useEffect([worktreePaths]) and no Date.now() key. The bugs are specific to the in-session /resume slash-command path.

View original on GitHub ↗