Skip to content
Case study · Forensic diagnosis · 2026

Forensic Diagnosis of a Closed-Source Hang: Claude Code --resume

A worked example of falsification-first diagnosis on a tool whose source we do not have. We could not read the code. So we reproduced the failure, falsified four classes of cause against direct measurement, isolated the one that survived, and handed the maintainers the triage artifact one of their own engineers would have built first. The filed report is a by-product. The method is the deliverable.

Investigation: May 2026, on Claude Code 2.1.126. Issue record re-verified against the tracker on 2026-08-19.

On Claude Code 2.1.126 (macOS arm64), claude --resume <session-id> failed to return to an interactive prompt for a multi-turn session, across multiple-minute waits. The same binary, same shell, same working directory, same hooks, started promptly when invoked without --resume. The failure was specific to the resume code path.

No stderr. No log line. No stack trace. The process spawned and consumed CPU during the freeze, which rules out a sleeping deadlock and points at active work that does not finish in time. This is the failure shape that is hardest to file against a tool you cannot see inside: there is nothing to copy-paste, no message to search, and the obvious first guess (session too big) turns out to be wrong.

Claude Code is distributed as a closed binary. There is no repository to git-blame, no failing line to point at, no internal telemetry to read. A standard bug report in this situation collapses to "it hangs, here is my session, please look." That report is correctly low-priority: it asks the maintainer to do the entire diagnosis.

The discipline a third party can still bring is falsification. You cannot prove what the cause is from the outside. You can prove what it is not, by measuring the artifacts you do control: the session file on disk, the process behavior under load, the hook timings, the environment. Every falsified class narrows the surviving space and turns "please look" into "the cause is in this code path, here is how to confirm it."

  • 2026-05-01 · Hermes Labs files anthropics/claude-code #55241 with the falsification log, the measured session profile, and a three-step triage plan.
  • 2026-05-01 · the duplicate-detection bot flags three candidates: #52995, #53011, #47508, and schedules auto-closure.
  • 2026-05-01 · we respond with differentiating evidence. #52995 attributes its hang to large total size (16 / 12 / 11 MB sessions) and very long single lines (max ~375 KB); the session here is 2.7 MB total, largest single line ≈58 KB, and the falsification log shows the data-shape cause was tested and ruled out by direct measurement. #53011 is a different command (/rewind). #47508 is a different symptom (post-compact context loss after a successful resume, not a hang). Auto-closure is prevented.
  • 2026-05-01 · sanitized repro JSONL attached: identifiers, timestamps, and oversize tool-result text removed, structural shape preserved.
  • 2026-06-01 · the issue auto-closes as stale after a period of inactivity. It was not closed as a duplicate, and there is no linked fix; the surviving hypothesis remains unconfirmed from the outside, exactly as the report stated.

Credit to the Claude Code maintainers: the duplicate-detection bot is a sensible defense against a noisy tracker, and the cluster of related resume-path reports it surfaced is real signal. The point of the differentiating response was not to argue with it, but to add the one thing a near-duplicate lacks: a measured profile that the existing reports do not cover.

Five hypotheses across distinct cause-classes were tested before naming a likely culprit. Four were falsified against direct evidence. The fifth survived, and the report is explicit that surviving falsification is not the same as positive confirmation, which cannot be reached from outside the binary.

H1 · Orphan process / lock contention. Falsified. Terminating every surviving process holder did not unblock subsequent resume attempts. If a lock were the cause, clearing the holder would clear the hang. It did not.

H2 · Data-shape / parse-stall. Falsified by measurement. The first guess a maintainer would reach for, and the one #52995 reported. All 1271 lines parse as valid JSON. No entry exceeds 500 KB; largest single line ≈58 KB, largest tool-result ≈25 KB, total tool-result content ≈252 KB. Zero image blocks, zero base64 substrings, zero malformed lines, zero orphaned tool_use in the trailing entries. The session is structurally clean and well under the sizes the existing large-file reports cite.

H3 · SessionStart hook hang. Partially falsified. Each hook was invoked manually with representative stdin and timed. No single hook accounts for the freeze; the symptom recurs even when the one variable-latency hook short-circuits, verified against that hook's own log. Hooks add perceived slowness in some runs but are not the recurring cause.

H4 · Runtime hydration / parse / render bottleneck on the resume path. Survives. The failure is specific to --resume; the non-resume start path is healthy. CPU is active during the freeze, consistent with work that does not complete in time rather than an idle deadlock. This is the surviving cause-class. From outside the binary, whether it is a genuine deadlock under specific conditions or merely very slow is not distinguishable without a stack-frame capture, and the report says so.

H5 · Environment / IDE / Node-version regression. Partially falsified. The symptom recurs across multiple terminal instances on the same machine, which weakens a terminal-specific or IDE-specific explanation. A cleanroom container reproduction was not performed, and the report names that as an open gap rather than asserting the hypothesis dead.

The shape of the work is the point. Each gate either kills a hypothesis with evidence the reader can check, or it states honestly what was not tested. There is no step where a guess is dressed up as a conclusion.

Because the cause cannot be confirmed from outside, the deliverable is not a fix. It is the set of inputs that let an engineer with source access confirm or kill H4 faster than they could from a raw session dump. Three confirmation tests, each designed to discriminate between the remaining possibilities:

1 · Profile under attachment. Resume against the sanitized repro with a profiler attached. Hot frames in JSONL parse, conversation-state hydration, or TUI buffer construction confirm H4 directly. Idle-on-event with no pending input would instead indicate a deadlock variant. This single test separates "slow" from "stuck."

2 · Bisect by session length. Truncate the repro to 25 / 50 / 75 percent and find the point at which resume becomes usable again. This isolates whether the cost is per-turn linear, super-linear, or threshold-triggered, which in turn tells the team which subsystem to instrument.

3 · Correlate against internal telemetry. Cross-reference the existing resume-path report cluster against instrumentation on parse-time, render-time, and context-priming-time. This is the one step only the maintainers can run, and the report says so plainly.

Alongside the tests: a sanitized, structure-preserving repro JSONL attached to the issue (~2.4 MB extracted), so the team reproduces against a clean artifact rather than a redacted screenshot. And a falsification log that pre-answers the questions a triager would otherwise ask back.

The report also notes a user-side mitigation in passing: a small script that reads the JSONL outside Claude Code and primes a fresh session with a summary of prior turns, recovering working continuity. It is named as a workaround, explicitly not a fix, so the underlying issue is not mistaken for resolved.

Most teams depend on closed-source AI tooling they cannot inspect: a hosted model, an agent runtime, a coding CLI, a managed retrieval service. When one of those fails silently, the reporter is usually stuck at "it broke, here is what I saw," which is not actionable and gets triaged accordingly. The capability demonstrated here is producing a defensible report against a system you cannot read, by falsifying from the outside until only the right code path is left standing.

The deliverable is the investigation, not the issue. A maintainer can adopt the falsification log and the three confirmation tests directly. A buyer can read the same artifact cold and judge whether the analysis is sound, what was measured, and what was honestly left open. The method does not require source access, special tooling, or vendor cooperation to be rigorous, only the discipline to test what you can and refuse to assert what you cannot.

Because the full report already lives on the public issue, nothing here is disclosed that is not already on the tracker. That is the intended end state of a forensic audit: the evidence trail is legible to anyone, and the conclusion stands on measurement rather than authority.

If a closed-source model, agent runtime, or AI tool in your stack is failing in a way you cannot reproduce, attribute, or file defensibly, that is the exact gap this method closes. We run falsification-first forensic diagnosis against systems we do not have the source to, and hand you an artifact a maintainer can act on.

Talk to us about a forensic diagnosis

Reference: anthropics/claude-code #55241 · Related cluster: #52995 · Upstream contributions: /open-source/contributions