Skip to content

[Pi] In-process parallel subagents can load Magic Context repeatedly and OOM in SessionManager.listAll #247

Description

@beilo

Short description

In-process @gotgenes/pi-subagents children inherit and initialize Magic Context. Four parallel children can trigger concurrent global Pi session scans and crash the parent Node process with heap OOM.

What happened?

Environment where this reproduced:

  • Magic Context: @cortexkit/pi-magic-context 0.32.4
  • Pi: @earendil-works/pi-coding-agent 0.81.1
  • Subagents: @gotgenes/pi-subagents 18.1.0
  • Node: 22.23.0
  • Platform: macOS arm64
  • Pi session store: about 392 JSONL sessions

Reproduction shape:

  1. Register both npm:@gotgenes/pi-subagents and npm:@cortexkit/pi-magic-context in Pi user packages.
  2. Start a normal persisted Pi session and let Magic Context finish its boot-quiet period.
  3. Start four background @gotgenes/pi-subagents children in parallel. Each child only needs one MCP tool call.
  4. The parent Pi process terminates with SIGABRT / JavaScript heap OOM.

Observed failure, sanitized:

Mark-Compact 4010.4 (4352.5) -> 3255.4 (4333.2) MB ... allocation failure
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

node::OOMErrorHandler
v8::internal::V8::FatalProcessOutOfMemory
v8::internal::JsonParser<...>::ParseJson
Builtin_JsonParse
node::fs::FSReqCallback::Resolve

This reproduced repeatedly with the OOM stack in JSON parsing during an async filesystem callback. Running without Magic Context removes this parallel-start crash path; Pi native compaction remains usable.

Why this appears different from #238

#238 covers Magic Context's own subprocess-based historian/dreamer children. Those children use MAGIC_CONTEXT_PI_SUBAGENT=1, and v0.33.0 adds isolated retry / extension controls for that runner.

@gotgenes/pi-subagents 18.x uses in-process createAgentSession() children instead:

  • its child loader intentionally inherits all parent extensions;
  • it emits subagents:child:session-created immediately before bindExtensions();
  • it does not set MAGIC_CONTEXT_PI_SUBAGENT=1 because process.env is process-global and unsafe as a per-child marker.

Magic Context's current Pi entry guard only checks MAGIC_CONTEXT_PI_SUBAGENT=1, so it does not recognize this child type. Each child can initialize Magic Context and reach Pi session discovery/backfill paths using SessionManager.listAll().

Pi's SessionManager.listAll() compounds the problem: session metadata search retains text from all user/assistant messages, and each call runs multiple concurrent file readers. Several in-process callers multiply peak memory.

I checked the v0.33.0 release and source. Its smaller lazy-loaded bundle reduces startup cost, but I did not find integration with @gotgenes/pi-subagents lifecycle events or a guard for external in-process child sessions. I have not claimed a runtime reproduction on 0.33.0; the confirmed crashes were on 0.32.4.

Expected behavior

Magic Context should not perform full extension initialization or global session backfill inside ephemeral external in-process subagent sessions.

Possible fix directions:

  • support a process-safe child-session marker from Pi session metadata or a documented lifecycle integration;
  • recognize @gotgenes/pi-subagents child session lifecycle without relying on a process-global environment variable;
  • at minimum, guard global SessionManager.listAll() / backfill work so child runtimes cannot fan it out concurrently;
  • add a regression test with multiple in-process child sessions binding extensions in parallel.

A complementary fix belongs in Pi core to bound SessionManager.listAll() memory, but skipping unnecessary Magic Context initialization in child sessions removes this specific multiplier.

Diagnostics

- Client: Pi
- Confirmed plugin version: 0.32.4
- Latest version inspected: 0.33.0
- Pi version: 0.81.1
- @gotgenes/pi-subagents: 18.1.0
- Node: 22.23.0
- Platform: macOS arm64
- Crash: SIGABRT, V8 heap OOM around 4.35 GB
- Workaround: uninstall `npm:@cortexkit/pi-magic-context` and use Pi native compaction

Plugin version

0.32.4

OpenCode version

N/A

Platform

macOS arm64

Client

Pi

Log output (optional)

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions