Skip to content

fix: stream-parse session JSONL to prevent OOM on large sessions#160

Open
sidbmw wants to merge 1 commit intoslopus:mainfrom
sidbmw:fix/oom-large-session-files
Open

fix: stream-parse session JSONL to prevent OOM on large sessions#160
sidbmw wants to merge 1 commit intoslopus:mainfrom
sidbmw:fix/oom-large-session-files

Conversation

@sidbmw
Copy link

@sidbmw sidbmw commented Feb 6, 2026

Summary

Fixes #526 — happy crashes with FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory on long-running Claude Code sessions.

Root Cause

Long-running Claude Code sessions (especially after context compaction via --continue) produce JSONL files of 500MB+. Two functions load the entire file into memory:

  1. readSessionLog() — uses readFile(path, 'utf-8') then .split('\n'), creating two copies of the entire file in memory
  2. claudeCheckSession() — uses readFileSync(path, 'utf-8').split('\n') just to check if one valid message exists

V8's default heap limit is ~4GB. A 650MB JSONL file becomes ~1.3GB as a UTF-16 JS string, then doubles with .split(), plus the parsed JSON objects — easily exceeding the heap.

Fix

  • readSessionLog(): Replace readFile() with createReadStream() + readline.createInterface() for line-by-line streaming. Memory stays flat (~50MB) regardless of file size. Also caps retained messages at 500 most recent — the mobile app only needs recent context, not the full history.

  • claudeCheckSession(): Replace readFileSync() with a 16KB partial read via openSync/readSync. Only need to find one valid JSON line to confirm session validity — no need to read the entire file.

Testing

Tested with a 650MB session JSONL file (long-running Claude Code session with multiple context compactions). Before: OOM crash within 60 seconds. After: starts successfully with ~50MB memory usage.

Test plan

  • Start happy --continue with a large session (500MB+ JSONL)
  • Verify no OOM crash on startup
  • Verify messages sync correctly to mobile app
  • Verify new messages appear in real-time after initial sync
  • Run existing claudeCheckSession.test.ts and sessionScanner.test.ts

🤖 Generated with Claude Code

Long-running Claude Code sessions (especially after context compaction)
produce JSONL files of 500MB+. Both `readSessionLog()` and
`claudeCheckSession()` load the entire file into memory with
`readFile()`/`readFileSync()`, causing V8 heap exhaustion (OOM crash).

Fixes:
- `readSessionLog()`: Replace `readFile()` with `createReadStream()` +
  `readline.createInterface()` for line-by-line streaming. Memory stays
  flat regardless of file size. Cap retained messages at 500 most recent.
- `claudeCheckSession()`: Replace `readFileSync()` with a 16KB partial
  read. Only need to find one valid JSON line to confirm session validity.

Fixes #526

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant