Skip to content

fix: yield event loop before sendAIMessage recursion to prevent OOM#1325

Merged
lewis617 merged 2 commits into
mainfrom
fix/sendaimessage-recursion-event-loop-yield
Jul 2, 2026
Merged

fix: yield event loop before sendAIMessage recursion to prevent OOM#1325
lewis617 merged 2 commits into
mainfrom
fix/sendaimessage-recursion-event-loop-yield

Conversation

@lewis617

@lewis617 lewis617 commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Problem

Memory caching made getAutoMemoryContent return synchronously on cache hits, turning sendAIMessage recursion into a pure microtask chain. This starved the event loop — the 50ms abort setTimeout (a macrotask) never fired, causing unbounded recursion (depth 2400+) and OOM.

Fix

await setImmediate before recursing to let macrotasks (abort signals, timers) process. Re-check abort status after yielding.

Verification

  • 3131 tests passed, 1 skipped (235 test files) at 1GB heap limit with coverage
  • Before fix: OOM at depth 2400+ with FATAL ERROR: Reached heap limit

Ref

lewis617 added 2 commits July 2, 2026 14:40
…hange

All MemoryService methods (getAutoMemoryContent, getUserMemoryContent,
readMemoryFile, getCombinedMemoryContent) now cache their results after
first read and return cached values on subsequent calls within the same
session. Cache is invalidated via clearCache() wired into the
onSessionIdChange callback in containerSetup.ts, which fires on clear,
compact, truncate, and restore.

- Convert all cache fields to null-sentinel pattern (string | null)
- Add _cachedAutoMemoryContent field
- Cache readMemoryFile() and getUserMemoryContent() independently
- Wire memoryService.clearCache() into onSessionIdChange callback
- Remove redundant clearCache() call in Agent.clearMessages()
- Add tests for caching and cache invalidation behavior
Memory caching made getAutoMemoryContent return synchronously on cache
hits, turning sendAIMessage recursion into a pure microtask chain. This
starved the event loop — the 50ms abort setTimeout (a macrotask) never
fired, causing unbounded recursion (depth 2400+) and OOM.

Fix: await setImmediate before recursing to let macrotasks (abort
signals, timers) process. Re-check abort status after yielding.

Ref: #1324 (recursive → iterative refactor)
@lewis617 lewis617 merged commit 8c8bd9a into main Jul 2, 2026
1 check passed
@lewis617 lewis617 deleted the fix/sendaimessage-recursion-event-loop-yield branch July 2, 2026 08:59
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