Repository navigation
SessionClient poll loop keeps retrying after session disposal ("Cannot invoke native session after disposal has started"), leaking memory until heap OOM #5263
Description
Describe the bug
A long-running interactive session gets slower and eventually crashes with FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory. On my machine this happens about once a week.
The cause appears to be an orphaned SessionClient poll loop. If a session is closed (for example, on /resume or when switching sessions) while an MCP server's OAuth flow is still running, the native session gets disposed. The JS-side poll loop does not stop, though. It keeps retrying forever, about once per second for each leaked loop:
[ERROR] SessionClient poll loop error (will retry): Error: Cannot invoke native session after disposal has started
Each leaked loop adds another stream of these errors, and heap usage climbs until V8 aborts at the ~4 GB limit.
Affected version
1.0.100 and 1.0.102 (both crashes happened on these builds)
Steps to reproduce the behavior
- Configure remote HTTP MCP servers that need OAuth (in my case Confluence and Datadog) and start an interactive session while their tokens are expired or invalid. Both servers return
401, which triggers the OAuth flow. - Close or switch the session (e.g.
/resumeanother session) while the OAuth/MCP reload is still in progress. - Keep the CLI process running. The log fills with
SessionClient poll loop errorabout once per second for each leaked loop, and the process OOMs after a few days.
Here is the log sequence at the moment the loop starts:
[INFO] Closing session <id>
[ERROR] [rust:rmcp::transport::worker] worker quit with fatal: Transport channel closed, when Client(OAuthChallenge { ... status_code: 401 ... })
[WARNING] [rust:mcp_engine::agent_host] HTTP 401 challenge (...) {"server":"confluence","error":"MCP authorization was cancelled"}
[WARNING] [rust:mcp_engine::agent_host] HTTP 401 challenge (...) {"server":"datadog","error":"MCP authorization was cancelled"}
[WARNING] [rust:copilot_runtime::session::mcp::session_host] MCP reload lock still held at disposal; draining the graph without it
[ERROR] [rust:copilot_runtime::shared_api::event_log] Failed to retry MCP servers after OAuth listener registration: {"error":"MCP session host is disposed"}
[ERROR] SessionClient poll loop error (will retry): Error: Cannot invoke native session after disposal has started
... (repeats forever)
Expected behavior
Disposing a session should stop its SessionClient poll loop, or the loop should exit on this error instead of retrying. Leaked loops should not pile up and exhaust the heap.
Additional context
- macOS 26.7.1 (Apple Silicon, arm64), iTerm2, zsh; installed via Homebrew cask
copilot-cli; Node v24.x embedded - Crash 1 (Oct 21): one process OOM'd after the loop had run for about a day.
- Crash 2 (Oct 27): two processes launched Oct 21 died within ~10 ms of each other after ~6 days of uptime.
- One was a V8 OOM (
node::OOMErrorHandler→abort, last GC:Mark-Compact 3871.5 (4102.0) -> 3851.3 (4086.0) MB). - The other was a SIGABRT raised from
uv_kill. - The OOM'd process had the error loop running continuously for 4+ days.
- One was a V8 OOM (
- Log volume:
- One process rotated through 5 × 10 MB log files containing ~72–75k of these error lines each.
- Another logged 3 identical errors per second, which suggests 3 leaked loops.
- Possibly related: OOM crash (
JavaScript heap out of memory) on long--resumesessions; crash dumps written into the user's cwd #4699, Frequent JavaScript heap out of memory #4725, Node.js OOM crash after ~37 min — 31,965 leaked async libuv handles (SEA ignores NODE_OPTIONS) #4686 (all OOM reports), but none of them identify this poll loop. - Workaround: restart the CLI periodically.
🤖 This issue was posted on behalf of Caleb Tenberge by GitHub Copilot CLI (Claude Opus 5.5/Anthropic).
Activity