[BUG] Hook output over 10K silently dropped from context with zero signal — memory plugins lose injected data invisibly

Status Open
Reported on v2.1.216
Maintainer reply None cached
Activity 8 comments · opened Aug 5, 2026

Problem
Hooks that emit more than 10,000 characters of additionalContext (internal persistHookOutput threshold, since v2.1.89) are persisted to a temp file and never injected into the model context. The hook receives no error, no warning, and no way to know its output was discarded.

Why this is worse than just a size cap
For long-term-memory plugins this is silent data loss on every session start:

  • Hook writes ~38KB of context → process exits 0 → nothing in context → the model starts with no memory.
  • No stderr warning, no marker in the hook JSON output, no log line — a plugin author cannot detect this from the hook side.
  • The user sees a normal-looking session and has no idea the injected context was dropped.

Requested
When output exceeds the threshold, give the hook (and the user) an explicit signal:

  1. A warning line on stderr and/or in the hook result, and/or
  2. A structured field in the hook JSON (e.g. additionalContextFile + truncated: true), and/or
  3. A persistent log entry.

That at least turns invisible loss into an observable condition that plugins can handle (chunk their own output, fail loudly, or surface it to the user).

Related

  • #42369 (docs say 50K, implementation is 10K) — closed as NOT_PLANNED
  • #70460 (SessionStart silently truncated) — open, but marked duplicate/stale

Environment: Claude Code 2.1.216, VS Code extension, macOS.

View original on GitHub ↗

3 Comments

JhouCode · 16 days ago

Confirming the behaviour on a different client and a different event, with one factual correction and a workaround that may help people stuck on this today.

Environment: Claude Code desktop CLI 2.1.229, Linux. UserPromptSubmit hook — not only SessionStart, which answers your open question about other hook types: it affects both.

Correction: the content is not discarded entirely. A ~2,000-character preview is injected, along with the file path. I measured 18,057 bytes emitted and ~2,000 delivered — 11% arrived. For the briefing/memory case that is worse than total loss: the model receives the beginning of the document and proceeds as if it had the whole thing, so the symptom is not "no memory", it is confidently partial memory. In my case half of the method layer arrived and none of the operational-rules layer.

Confirmed on the runtime side: the mechanism strings ("Output too large (…). Full output saved to: …", "Preview (first …)") are present in the build, and the preview-size constant resolves to 2000.

How this stayed invisible to me: three green instruments at once — the hook success status, the hook's own log, and its self-test. All three were answering "did the hook run?". The question that mattered was "did the content arrive?". Only a byte-for-byte diff between the script's output and the delivered context separated them. Which is exactly your request #1.

A workaround that works today, without waiting for a fix: the cap applies per registered command, not per event. Registering the same script N times, each instance emitting one slice under the cap, delivers everything in the same event. Measured with two 5,149-character probes registered together: both arrived intact — 10,298 characters in a single turn. It is the "chunk their own output" you mention, minus the need for a signal, and the docs do not make it obvious.

Two caveats for anyone using it: arrival order is not guaranteed (blocks arrive out of order, so each one has to identify itself), and chunking does not solve the problem you raised — it only avoids falling into it. Without a signal, anyone whose payload grows past the cap still finds out the hard way.

On the 50K discrepancy you cite from #42369: the current docs already state 10,000 (JSON output section), so that part seems to have been corrected in the text. What is still missing is item 1 of your request — that documented behaviour be signalled at runtime.

JhouCode · 16 days ago

Adding the report lineage, since it is scattered and no single issue lists it. This is offered as triage context, not as a complaint about handling.

The same behaviour has been reported independently at least five times since January:

| issue | date | what it reported | outcome |
|---|---|---|---|
| #17944 | Jan 13 | BASH_MAX_OUTPUT_LENGTH ignored — large output spills to disk regardless of the setting | closed not planned by the stale bot |
| #41799 | Apr 1 | [DOCS] hooks docs omit the spill-to-disk + preview behaviour | still open, no replies |
| #42369 | Apr 2 | SessionStart output spills at ~10K, not the documented 50K | flagged duplicate by bot → closed inactive → locked |
| #70460 | — | SessionStart output silently truncated at 10KB | closed by the stale bot |
| #84021 | Aug 5 | this one | open |

Adjacent, from the memory side: #82056 (a session cannot tell whether its index loaded whole, truncated, or not at all).

Two things stand out from the set, and both are mechanical rather than editorial:

None of the closed ones were closed on technical grounds. They aged out. The bot flags, warns, closes, and eventually locks. So "already reported and rejected" would be the wrong reading — it was never assessed either way.

None of the reporters found each other. Five people hit the same failure independently, each one alone on their thread, each discovering it the hard way. That is the strongest argument for request #1 in this issue: a runtime signal would have made every one of those five a five-minute diagnosis instead of a private investigation.

I have no visibility into triage priorities and I am not arguing about them. I am adding the history because it is the piece that was missing, and because activity is what keeps this thread from meeting the same automated end as the other four.

JhouCode · 16 days ago

Correcting my own lineage comment above: there are seven, not five. I searched by title and missed two that are only referenced inside another thread's comments. Both are @permaevidence's, and they are the most directly on point.

| issue | date | what it reported | outcome |
|---|---|---|---|
| #17944 | Jan 13 | BASH_MAX_OUTPUT_LENGTH ignored — large output spills to disk regardless of the setting | closed not planned by the stale bot |
| #41799 | Apr 1 | [DOCS] hooks docs omit the spill-to-disk + preview behaviour | open, no maintainer reply |
| #42369 | Apr 2 | SessionStart spills at ~10K, not the documented 50K | flagged duplicate → closed inactive → locked |
| #50571 | Apr 19 | "Is there a way to configure the persistHookOutput 10,000-character threshold?" | **closed not planned by the stale bot, May 31 |
|
#51537 | Apr 21 | [FEATURE] raise, remove or make the 10,000-char cap configurable | open, labelled duplicate + stale** |
| #70460 | — | SessionStart output silently truncated at 10KB | closed by the stale bot |
| #84021 | Aug 5 | this one | open |

Two things those two add that I did not have.

The "is it configurable?" question was asked formally, and closed without an answer. That is #50571. So when several of us report "I could not find an env var or a settings key", that is not for lack of looking — someone asked the question directly, on the tracker, and the thread aged out. Worth knowing before anyone re-derives it.

The constant has been read independently, four months apart, under two different names. @permaevidence identified it as PE4 = 1e4 in v2.1.89 (comment on #41799, Apr 21), noting it was renamed but unchanged through v2.1.116. I read it as BTK = 1e4 in a current build, with the preview size at 2000, without knowing that comment existed. Same constant, same value, minified name changed between versions — which is a useful detail for anyone trying to verify this from a shipped binary and finding a different symbol.

That also revises what I wrote earlier about the 50K/10K discrepancy being settled: the documentation now says 10,000, but the underlying request in #51537 — raise it, remove it, or make it configurable — is still open and still labelled stale, and it is a different ask from the signalling requested here. Both can be true: the value is correctly documented now, and it is still both unsignalled (this issue) and unconfigurable (#51537).

Seven reports, one author with two of them, no maintainer reply on any, and every closure automated. Posting the corrected set so the next person does not have to reassemble it.

Showing cached comments. Read the full discussion on GitHub ↗