Bug: app-server can retain orphaned threads after last-subscriber disconnect
Summary
I’m opening this issue both to report the bug and to ask what the expected external contributor flow is for getting the patch upstream.
Bug
When the last subscribed client connection disappears unexpectedly, app-server removes the connection bookkeeping but can leave the underlying thread loaded.
That diverges from explicit thread/unsubscribe, which already unloads the thread when the last subscriber leaves.
In practice, this leaves zero-subscriber threads resident longer than necessary and can also leave stale app-server bookkeeping behind in late-shutdown edge cases.
Why this matters
This is a real retention path for subagent-heavy and long-lived desktop sessions:
- unexpected disconnects can strand loaded threads with no subscribers
- stale thread/watch state can outlive the core thread
- the behavior is inconsistent with the existing explicit unsubscribe path
Proposed fix
The patch I prepared is intentionally narrow and stays inside codex-rs/app-server.
Behaviorally it does three things:
ThreadStateManager::remove_connectionreturns theThreadIds that became orphaned when the disconnect removed the last subscriber.CodexMessageProcessor::connection_closedroutes those orphaned threads through the same unload path already used by explicitthread/unsubscribe.- Late
ShutdownCompleteevents reconcile stale thread/watch state if the core thread was already removed, while preserving the existingthread/closedsemantics for that path.
Scope:
- no protocol changes
- no schema changes
- no docs changes
- no generated files
Validation
I implemented the patch in my fork and validated it locally with:
cargo test -p codex-app-serverjust fmtjust fix -p codex-app-server
Per repo rules, I did not rerun tests after the final just fix.
Branch / commit
- Fork:
agustif/fast-codex - Branch:
fix/app-server-orphaned-thread-unload - Commit:
8b73536b54daa6c06b156b7ed5e9f1d8801662bb - Compare URL: https://github.com/openai/codex/compare/main...agustif:fix/app-server-orphaned-thread-unload?expand=1
Tests included in the patch
- non-orphan disconnect preserves the listener when another subscriber remains
- closed connections cannot be auto-subscribed back into a thread
- last-subscriber disconnect reports the orphaned thread id
ShutdownCompleteafter forced unload clears stale state and emitsthread/closed
Contributor-flow question
I attempted to create the upstream PR with GitHub CLI and got:
GraphQL: agustif does not have the correct permissions to execute CreatePullRequest
If external contributions are accepted here, could you clarify the expected flow or the permission/setup needed to submit this patch as a PR?
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗