Bug: app-server can retain orphaned threads after last-subscriber disconnect

Open 💬 2 comments Opened Mar 9, 2026 by agustif
💡 Likely answer: A maintainer (etraut-openai, contributor) responded on this thread — see the highlighted reply below.

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:

  1. ThreadStateManager::remove_connection returns the ThreadIds that became orphaned when the disconnect removed the last subscriber.
  2. CodexMessageProcessor::connection_closed routes those orphaned threads through the same unload path already used by explicit thread/unsubscribe.
  3. Late ShutdownComplete events reconcile stale thread/watch state if the core thread was already removed, while preserving the existing thread/closed semantics 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-server
  • just fmt
  • just fix -p codex-app-server

Per repo rules, I did not rerun tests after the final just fix.

Branch / commit

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
  • ShutdownComplete after forced unload clears stale state and emits thread/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?

View original on GitHub ↗

2 Comments

etraut-openai contributor · 5 months ago

Thanks for the bug report. We no longer generally accept external PRs. They take longer for us to code review than it would take us to fix the bug ourselves (using Codex). This looks like a pretty low-impact bug. We have many higher-priority bug fixes, so this may go a while before getting fixed.

agustif · 5 months ago
Thanks for the bug report. We no longer generally accept external PRs. They take longer for us to code review than it would take us to fix the bug ourselves (using Codex). This looks like a pretty low-impact bug. We have many higher-priority bug fixes, so this may go a while before getting fixed.

alright sounds good thank you