fix: cap journal body at 64KB to prevent Invalid string length crash on GET /__aimock/journal#308
Merged
Conversation
…/__aimock/journal
When journal entries retain large LLM request bodies (500KB+), serializing
1000 entries via JSON.stringify exceeds V8's ~512MB string limit, crashing
the server with RangeError: Invalid string length. Cap stored bodies at
64KB at the journal source (journal.add()), replacing oversized bodies with
a truncation marker { __aimock_truncated: true, originalByteSize: N }.
This bounds aggregate journal memory to ~64MB at maxEntries=1000 and
protects all downstream consumers of journal.getAll(), not just /journal.
Also unifies catch-all error logging to include err.stack unconditionally
for production observability (removes [DIAG] tag from diagnosis branch).
Regression test: 1000 × 100KB entries, JSON.stringify must not throw.
commit: |
…ltibyte regression test - Change `capBody` gate from `serialized.length` (UTF-16 code units) to `Buffer.byteLength(serialized, "utf8")` to match JOURNAL_BODY_CAP_BYTES constant name and correctly truncate multibyte content (CJK/emoji) whose code-unit count falls under the 64 KB threshold but whose UTF-8 byte size does not. - Rewrite the JOURNAL_BODY_CAP_BYTES JSDoc: the old comment incorrectly claimed a ~64 MB aggregate bound enforced by the cap; only the request body is capped — headers/path are retained uncapped but bounded by Node's default ~16 KB max HTTP header size. The updated doc accurately describes the actual mechanism. - Add regression test: CJK body (22,000 × U+4E2D = ~66 KB UTF-8, ~22 K code units) proves the byte-based gate truncates content the old code-unit gate missed. Confirmed RED against the old gate, GREEN after this fix.
jpr5
marked this pull request as ready for review
July 17, 2026 17:23
Merged
jpr5
added a commit
that referenced
this pull request
Jul 17, 2026
## Release v1.37.2 (patch) Cuts a patch release for the journal body cap + error stack instrumentation fix merged in #308. ### Version bump `package.json` `1.37.1` → `1.37.2`. Also bumps `packages/aimock-pytest/src/aimock_pytest/_version.py` `AIMOCK_VERSION` to `1.37.2` (pins the pytest harness to the released npm version). ### CHANGELOG ## [1.37.2] - 2026-07-17 ### Fixed - Prevent `Invalid string length` crash on `GET /__aimock/journal` by capping retained request bodies at 64 KB (#308) ### Changed - Log full error stack on unhandled request errors for prod diagnosis (#308) ### Quality gate - `pnpm run format:check` — pass - `pnpm run lint` — pass - `tsc --noEmit` — pass - `pnpm test` — 4549 passed (154 files) - `pnpm run build` — pass
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
GET /__aimock/journalserializes the full journal (up to 1000 entries) viaJSON.stringify. When LLM request bodies are large (~530 KB each in prod-like agentic traffic), the aggregate JSON string exceeds V8's 512 MB limit →RangeError: Invalid string length. The catch-all logged onlyerr.message, so no stack ever appeared in prod logs.Real stack (locally reproduced):
Fix
Cap the retained request-body size at 64 KB per journal entry at the journal source (
journal.add()). Bodies exceeding the cap are replaced with a truncation marker{ "__aimock_truncated": true, "originalByteSize": N, "note": "..." }. This bounds aggregate journal memory to ~64 MB at maxEntries=1000 (well under 256 MB), and protects all downstream consumers ofjournal.getAll(), not just/journal.Also unifies the catch-all error log to include
err.stackunconditionally for production observability (removes[DIAG]debug tag from the diagnosis branch).RED (pre-fix, crash confirmed locally)
Repro: 1000 × 530 KB POST bodies →
GET /__aimock/journal→RangeError: Invalid string lengthGREEN (post-fix, no crash)
Regression test
Added to
src/__tests__/journal.test.ts—"journal body cap (ISL regression)"describe block with 3 tests:replaces oversized bodies with a truncation marker— 100 KB body → marker storedretains bodies that are within the 64 KB cap— small body → stored as-isJSON.stringify(journal.getAll()) does not throw with 1000 large-body entries— 1000 × 100 KB bodies, must not throwPre-push checks