You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When the last app-server websocket connection drops without sending thread/unsubscribe, the loaded thread remains in memory and its MCP child process stays alive.
The root cause appears to be that connection_closed removes connection bookkeeping but does not unload threads that now have zero subscribers. thread/unsubscribe already has the correct unload path, but disconnects do not go through it.
Impact
In the desktop app this lets MCP-backed threads accumulate under the long-lived app-server process. Over time that leaves large numbers of retained biz-cosense-mcp-server, playwright-mcp, and wrapper processes alive after their owning connections are gone.
Reproduction
A deterministic regression test can reproduce this with a real stdio MCP child process:
Start app-server in websocket mode with a stdio MCP server configured.
Connect websocket client A and initialize it.
Start a thread so the MCP stdio child process is launched.
Verify the thread appears in thread/loaded/list and the MCP child process is alive.
Close websocket client A without sending thread/unsubscribe.
Reconnect as websocket client B and inspect thread/loaded/list.
Expected
The last disconnect should unload the orphaned thread.
The MCP stdio child process should exit.
A subsequent thread/loaded/list should be empty.
Actual
The thread remains loaded after the last websocket disconnect.
The MCP stdio child process stays alive.
The new regression test times out waiting for the child process to exit.
Evidence
Before the fix, this targeted test fails:
mise exec rust@1.93.0 -- cargo test -p codex-app-server websocket_disconnect_unloads_thread_and_its_mcp_process -- --nocapture
Failure mode:
wait_for_process_exit(&pid) times out after websocket close.
Reconnecting still shows the thread in thread/loaded/list.
Acceptance Criteria
connection_closed unloads threads whose last subscriber disappeared.
The disconnect path reuses the same thread teardown logic already used by thread/unsubscribe.
A regression test covers websocket disconnect cleanup with a real stdio MCP child process and passes.
Existing thread_unsubscribe tests continue to pass.
I think this issue is still worth keeping open because it narrows one concrete app-server cause with a deterministic repro and tested fix:
when the last websocket client disconnects without thread/unsubscribe, connection_closed drops connection bookkeeping but does not unload the thread that now has zero subscribers
that leaves the thread loaded and keeps its MCP stdio child process alive
the fix branch here adds a regression test for that exact websocket-disconnect path and routes it through the existing thread teardown logic
So my read is:
#14950 is likely the broader user-facing symptom tracker for the desktop app
this issue documents one specific root cause / fixable path under that umbrella
Fix branch and compare are here if useful for triage:
Activity
github-actions commentedon Apr 3, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
kmatsunami commentedon Apr 3, 2026
Tested fix is available here:
0f73152d17b4a42f28a884f0a35eea123a16256cI could not open a PR because
openai/codexis configured withpull_request_creation_policy: collaborators_only.Verified locally:
mise exec rust@1.93.0 -- cargo test -p codex-app-server websocket_disconnect_unloads_thread_and_its_mcp_process -- --nocapturemise exec rust@1.93.0 -- cargo test -p codex-app-server removing_last_connection_returns_orphaned_thread -- --nocapturemise exec rust@1.93.0 -- cargo test -p codex-app-server thread_unsubscribe -- --nocapturekmatsunami commentedon Apr 3, 2026
I checked the duplicate suggestions.
Closest related issues:
stale MCP helper process trees)Ctrl+ZI think this issue is still worth keeping open because it narrows one concrete app-server cause with a deterministic repro and tested fix:
thread/unsubscribe,connection_closeddrops connection bookkeeping but does not unload the thread that now has zero subscribersSo my read is:
#14950is likely the broader user-facing symptom tracker for the desktop appFix branch and compare are here if useful for triage:
etraut-openai commentedon Apr 3, 2026
Closing in favor of #14950.