Skip to content

Persistent orphaned subagents, missing lifecycle controls, and eventual session freezes #19197

Open

Description

@vzd3v

What version of Codex CLI is running?

0.123.0

What subscription do you have?

Pro+ ($200)

Which model were you using?

gpt-5.4 high

What platform is your computer?

Ubuntu 24.04.4 LTS (GNU/Linux 6.8.0-90-generic x86_64)

What terminal emulator and version are you using (if applicable)?

tmux

What issue are you seeing?

Summary

Codex leaks subagents and does not manage their lifecycle reliably. After enough subagents accumulate, Codex itself hits the too many subagents launched limit and attempts cleanup, but usually terminates only 1–3 subagents. The remaining stuck/orphaned subagents are no longer visible to it, continue to count against the limit, and block further launches. If I ask the main thread to clean up inactive subagents, the result is the same: only partial cleanup, with the remaining subagents still stuck and still counted. Over time, the session can hard-freeze mid-task and must be killed manually.

This does not look like simple memory exhaustion: free RAM is still available and swap is large when the freeze happens.

This is not an MCP/apps issue.
I had already disabled apps explicitly in codex-config.toml because MCP/apps initialization was previously getting stuck indefinitely:

[features]
multi_agent = true
apps = false

Actual behavior

  • Subagents can remain stuck after their work is finished.
  • Codex eventually reaches its own subagent limit.
  • When this happens, Codex may try to clean up automatically, but usually terminates only 1–3 subagents.
  • The remaining stuck/orphaned subagents are not properly visible to Codex anymore.
  • Those invisible subagents still count against the launch limit.
  • Asking the main thread to clean up inactive subagents produces the same partial cleanup.
  • Entering a subagent through /agents does not provide a way to terminate it.
  • There are no usable user-facing lifecycle controls for subagents.
  • In long-running degraded sessions, Codex can hard-freeze and must be killed manually.
  • This can happen even with plenty of free RAM and large swap available.
Image

What steps can reproduce the bug?

  1. Use a session heavily with subagents.
  2. Let Codex spawn multiple subagents across tasks.
  3. After some time, some subagents remain alive after their useful work is already finished.
  4. Codex reaches the subagent limit and starts failing with too many subagents launched.
  5. Codex attempts cleanup on its own, but usually removes only 1–3 subagents.
  6. The remaining stuck/orphaned subagents are not cleaned up and appear to be no longer visible to Codex.
  7. Asking the main thread to clean up inactive subagents leads to the same partial result.
  8. New subagents still cannot be launched reliably.
  9. If the session keeps running in this degraded state, Codex may eventually freeze and require a manual kill.

What is the expected behavior?

  • Codex should track all active subagents accurately.
  • Finished, inactive, or orphaned subagents should remain visible until fully cleaned up.
  • Automatic cleanup should remove all inactive/orphaned subagents, not just 1–3.
  • Manual cleanup from the main thread should also remove all inactive/orphaned subagents reliably.
  • Stuck subagents should not continue counting against the limit after cleanup is requested.
  • /agents should allow explicit termination of the current subagent.
  • Users should have direct lifecycle controls for listing, terminating, force-killing, and resyncing subagents.
  • Leaked subagents should not be able to degrade the session into a blocked or frozen state.

Impact

This is a serious blocker for subagent-heavy workflows. Instead of enabling parallel decomposition, the current behavior causes degraded sessions, blocked launches, repeated manual recovery attempts, and eventual session loss.

Related issue

There is also a separate delegation quality problem: even when the main session runs on GPT-5.4, Codex often delegates unsuitable tasks to Codex 5.2, including research or spec writing, and often adds unnecessary restrictive instructions. This is separate, but it makes the subagent failure mode worse.

Activity

  1. added
    CLIIssues related to the Codex CLI
    subagentIssues involving subagents or multi-agent features
    on Apr 23, 2026
  2. vzd3v commented on Apr 23, 2026

    @vzd3v
    Author

    Potential duplicates detected. Please review them and close your issue if it is a duplicate.

    Powered by Codex Action

    I reviewed the suggested duplicates.

    This is not an MCP/apps issue on my side.

    I had already disabled apps explicitly in codex-config.toml because MCP/apps initialization was previously getting stuck indefinitely:

    [features]
    multi_agent = true
    apps = false
  3. pavbar commented on May 10, 2026

    @pavbar

    Adding a narrower abort-path reproduction here instead of opening another issue, because this appears to fit the same orphaned-subagent lifecycle family. I searched existing issues first for close_agent, wait_agent, subagent, and turn_aborted.

    My read of the related issues: #18335 is the closest earlier technical report but is closed and framed around spawn-slot leaks in persistent sessions; #17574/#19092 are mostly MCP helper process leaks; #19344 is broader app freeze/session loss; and #20908 is recovery messaging. This issue looks like the best active place for evidence about a subagent that has a turn_aborted terminal event but still cannot be waited/closed correctly.

    What version of the Codex App are you using?

    I did not capture the About Codex dialog version during the repro. The affected session metadata reports:

    cli_version: 0.130.0-alpha.5
    originator: Codex Desktop
    

    What subscription do you have?

    ChatGPT Pro.

    What platform is your computer?

    Darwin 25.4.0 arm64 arm
    

    What issue are you seeing?

    A Codex Desktop subagent that was interrupted/aborted became a stale lifecycle handle:

    • wait_agent returned completed statuses for other subagents, but no terminal status for the interrupted one.
    • close_agent against the interrupted/orphaned subagent blocked indefinitely until I manually interrupted the parent turn.
    • The stale subagent still appeared in local session state, but no longer behaved like a closeable/waitable agent.

    This is a narrower form of the orphaned-subagent behavior described in this issue: the trigger was an explicit interrupted/aborted subagent turn.

    What steps can reproduce the bug?

    1. In Codex Desktop, spawn several explorer subagents from one parent thread.
    2. Let several subagents complete normally.
    3. Interrupt/abort one subagent turn before it reaches a normal final result.
    4. From the parent thread, call wait_agent for all subagent ids.
    5. Observe that completed agents return terminal statuses, but the interrupted agent returns no status.
    6. Call close_agent for the interrupted agent id.
    7. Observe that close_agent hangs until the parent turn is manually interrupted.

    Affected identifiers from my repro:

    parent_thread_id: available on request
    subagent_id: ...fd638
    subagent_turn_id: ...dd72
    thread_name: <redacted private task name>
    

    Local session index entry for the affected subagent:

    {"id":"...fd638","thread_name":"<redacted private task name>","updated_at":"<redacted timestamp>"}

    Session file tail for that subagent:

    {"type":"event_msg","payload":{"type":"task_started","turn_id":"...dd72"}}
    {"type":"response_item","payload":{"type":"message","role":"user","content":[{"type":"input_text","text":"<turn_aborted>..."}]}}
    {"type":"event_msg","payload":{"type":"turn_aborted","turn_id":"...dd72","reason":"interrupted"}}

    What is the expected behavior?

    • wait_agent should surface a terminal state for interrupted/aborted subagents, for example aborted, cancelled, or closed.
    • close_agent should be idempotent and return quickly for already-aborted agents.
    • If the backend cannot resolve the subagent state, close_agent should return a structured timeout/error instead of blocking the parent turn indefinitely.

    Additional information

    Observed in a normal Codex Desktop subagent workflow; sharing reproduction details and local state evidence per the repo contributing guidance.

  4. sockpuppet9000 commented on May 13, 2026

    @sockpuppet9000

    Adding one more concrete abort-path data point because it appears to match the same orphaned-subagent lifecycle failure.

    What version of Codex CLI is running?

    cli_version: 0.130.0-alpha.5
    originator: Codex Desktop
    

    I did not capture the About Codex app dialog version during the repro.

    What subscription do you have?

    ChatGPT Pro.

    What platform is your computer?

    macOS 26.3.1 (a), build 25D771280a
    Darwin 25.3.0 arm64
    

    What issue are you seeing?

    A spawned subagent entered an interrupted/aborted state and then behaved like a stale runtime handle:

    • The subagent rollout contains only startup plus an aborted turn marker, not a normal assistant result.
    • close_agent submitted shutdown and marked the persisted spawn edge as closed.
    • The runtime did not finish teardown cleanly.
    • Hours later, the app-server still treated the same subagent thread as a running thread with active_turn_status=Interrupted.
    • A later cleanup attempt that included close_agent for this subagent did not return before the parent turn was manually interrupted.

    Identifiers, partially redacted:

    parent_thread_id: 019e18ff...7454
    subagent_id:     019e2054...e457
    subagent_turn_id:019e2054...a5b
    

    Subagent rollout tail:

    session_meta
    event_msg/task_started
    response_item/message <turn_aborted> The user interrupted the previous turn on purpose...
    event_msg/turn_aborted
    

    Relevant logs for the subagent:

    2026-05-13 08:05:01Z DEBUG codex_core::session::handlers
      Submission { op: Shutdown }
    
    2026-05-13 08:05:01Z TRACE codex_core::tasks
      aborting running task task_kind=Regular sub_id="019e2054...a5b"
    
    2026-05-13 08:05:01Z WARN codex_core::tasks
      task 019e2054...a5b didn't complete gracefully after 100ms
    
    2026-05-13 11:09:31Z DEBUG codex_app_server::request_processors::thread_lifecycle
      composing running thread resume response ... active_turn_present=true
      active_turn_id=Some("019e2054...a5b")
      active_turn_status=Some(Interrupted)
    

    Persisted spawn edge state after the failed/partial cleanup:

    child_thread_id: 019e2054...e457
    status: closed
    

    So the local DB state and the runtime state appear inconsistent: the edge is already closed, but the app-server still sees an interrupted active turn for that thread.

    What steps can reproduce the bug?

    1. In Codex Desktop, spawn one or more subagents from a parent thread.
    2. Interrupt/abort one subagent turn before it reaches a normal final assistant result.
    3. From the parent thread, call close_agent for the interrupted subagent.
    4. Observe shutdown beginning, but the child task does not complete gracefully.
    5. Later, inspect app-server/session state or try cleanup again.
    6. Observe the subagent can still appear as a running/interrupted thread even though the spawn edge is persisted as closed, and close_agent can block instead of returning quickly.

    What is the expected behavior?

    • Interrupted/aborted subagents should have a model-visible terminal lifecycle state.
    • close_agent should be idempotent and should return quickly for already-interrupted or already-persisted-closed subagents.
    • If runtime teardown cannot complete, close_agent should return a structured timeout/error instead of blocking the parent turn indefinitely.
    • Persisted spawn-edge state and app-server in-memory/runtime state should not diverge in a way that leaves an uncloseable stale handle.
  5. TacoRocket commented on May 22, 2026

    @TacoRocket

    Adding a current-version data point from Codex Desktop, but this appears to be the shared Codex core / app-server multi-agent lifecycle path rather than a GUI-only problem.

    Version / surface

    codex-cli 0.132.0
    Codex Desktop app
    macOS / Darwin arm64
    

    The installed Desktop app includes the bundled codex CLI binary and exposes the same core/app-server protocol. The relevant symbols and source paths are in codex-rs/core/src/agent/* and codex-rs/core/src/tools/handlers/multi_agents*, not only in the GUI layer.

    What I observed

    In a long-running local project session that uses subagents heavily, completed subagents continued to count against the subagent pool until the parent explicitly called close_agent.

    wait_agent observed completion, but did not release the slot. After enough completed-but-not-closed workers accumulated, new spawn_agent calls hit the pool limit even though the workers had already returned their final handbacks.

    This matters for real project workflows where subagents are used to isolate risky implementation/review slices. Some examples that do not require project-specific details:

    • DB/storage slices where a worker may have opened local test processes or held a write lock during validation.
    • API/MCP integration tasks where worker cleanup needs to preserve evidence/history but should not strand a slot forever.
    • Long-running supervisor/service work where the parent can compact or run out of tool budget before issuing all expected close calls.

    I agree with earlier maintainer comments that a completed agent may still have background terminal processes or MCP connections, so blindly deleting thread state is not safe. The issue is that the only safe release path appears to be explicit close_agent, and the parent model can miss that call.

    Code-level read

    From rust-v0.132.0:

    • AgentControl::spawn_agent_internal reserves a pool slot with self.state.reserve_spawn_slot(config.agent_max_threads).
    • AgentRegistry::reserve_spawn_slot checks/increments total_count and returns AgentLimitReached when the limit is hit.
    • AgentRegistry::release_spawned_thread is the release path that removes the spawned agent from the active tree and decrements total_count.
    • release_spawned_thread is called from AgentControl::shutdown_live_agent, which is reached by AgentControl::close_agent, and from the InternalAgentDied cleanup path.
    • Normal completion maps TurnComplete to AgentStatus::Completed(...).
    • is_final treats Completed, Errored, Shutdown, and NotFound as final, but normal final status does not call release_spawned_thread.
    • wait_agent observes final status and emits waiting events; it does not close or release the agent.

    Relevant paths/functions:

    codex-rs/core/src/agent/registry.rs
      AgentRegistry::reserve_spawn_slot
      AgentRegistry::release_spawned_thread
    
    codex-rs/core/src/agent/control.rs
      AgentControl::spawn_agent_internal
      AgentControl::shutdown_live_agent
      AgentControl::close_agent
      AgentControl::maybe_start_completion_watcher
    
    codex-rs/core/src/agent/status.rs
      agent_status_from_event
      is_final
    
    codex-rs/core/src/tools/handlers/multi_agents*/wait.rs
    codex-rs/core/src/tools/handlers/multi_agents*/close_agent.rs
    

    Thread APIs are not an equivalent workaround

    I also checked the app-server surface. thread/unsubscribe only unsubscribes a connection from a thread. thread/archive archives and tears down normal thread state. Neither is exposed as the same operation as AgentControl::close_agent, and neither should be recommended as a slot-release workaround unless it is explicitly wired through the agent close/release path.

    Manual mutation/deletion/archive of thread state would risk losing UI/history/evidence and bypassing the only path that currently decrements the multi-agent pool counter.

    Requested fix shape

    The safest behavior would be to reap finalized spawned agents before enforcing the pool limit, while preserving history/result visibility and using the same release semantics as close_agent/shutdown_live_agent.

    If automatic reaping is too risky because Completed can still have live terminals or MCP connections, a narrower fix would be a safe explicit command/API that closes completed agents through AgentControl::close_agent, for example:

    • list open spawned agents with current status and slot-count status,
    • close all finalized spawned agents,
    • close one finalized spawned agent by path/id,
    • return structured errors/timeouts for interrupted or uncloseable agents.

    That would avoid relying on the model remembering every close_agent call and would avoid unsafe thread/archive / thread/unsubscribe workarounds.

    Tests that would cover this

    • Spawn up to the limit, complete at least one subagent, and verify a later spawn either succeeds after safe reap or returns a recovery surface listing closeable finalized agents.
    • Verify wait_agent completion observation does not silently lose result/history.
    • Verify close_agent remains the path that releases the pool slot.
    • Verify thread/archive and thread/unsubscribe are not treated as equivalent slot release.
    • Verify interrupted/errored subagents either close idempotently or return a bounded structured error instead of hanging the parent turn.
  6. TacoRocket commented on May 22, 2026

    @TacoRocket

    I prepared a validated reference patch on a fork. Given the repository contribution policy, this is not an unsolicited PR request; it is included here so maintainers can inspect the proposed behavior concretely if useful:

    main...TacoRocket:codex:subagent-finalized-slot-recovery

    Patch shape:

    • Reap finalized spawned leaf agents inside AgentControl before retrying agents.max_threads slot reservation.
    • Add model-side close_completed_agents to close every eligible finalized spawned leaf through the same close path.
    • Add app-server/user/hook API agent/closeCompleted with { closedCount } response, so cleanup is not only model-invoked.
    • Preserve history/results; do not use archive/delete/unsubscribe for slot release.
    • Skip running/interrupted agents and finalized parents with live descendants.

    Validation in the fork:

    • just fmt
    • just write-app-server-schema
    • cargo test -p codex-core close_completed_agents
    • cargo test -p codex-core finalized
    • cargo test -p codex-core multi_agent_feature_selects_one_agent_tool_family
    • cargo test -p codex-core multi_agent_v2_namespace_is_ignored_without_provider_namespace_support
    • cargo test -p codex-app-server-protocol agent_close_completed
    • cargo test -p codex-app-server-protocol schema_fixtures
    • cargo test -p codex-app-server agent_close_completed

    Happy to adapt, split, or submit this as a PR if a Codex maintainer invites that path.

  7. Keesan12 commented on Jun 6, 2026

    @Keesan12

    One fix shape that helped us on a similar runtime was to separate "thread still exists" from "child is still admitted to run again."

    For interrupted children, we force a bounded terminal receipt even if full teardown is still in flight:

    • child_id
    • last_progress_at
    • last_verifier_delta
    • halt_reason
    • next_attempt_allowed
    • resource_reconciliation = pending|done|failed

    If teardown cannot finish inside the close path, the child moves to a terminal-but-unreconciled state and stops counting against fresh work admission. A background janitor can then reconcile leftover MCP/process resources without leaving the operator stuck behind a handle that is both dead and still blocking capacity.

    That design made two failure modes much easier to reason about for us:

    1. interrupted children stopped poisoning the scheduler forever
    2. operators could see whether they were waiting on real cleanup or on a bookkeeping bug

    We've been hitting the same family of issues in MartinLoop, and the biggest improvement came from making the terminal receipt explicit instead of letting "close" be the only source of truth.

  8. vzd3v commented on Jun 19, 2026

    @vzd3v
    Author

    It’s been almost two months since April 24, but it still doesn’t look fixed. Everything is the same in codex-cli 0.140.0

    Image
  9. safal207 commented on Jun 24, 2026

    @safal207

    This issue maps closely to LS work on explicit agent lifecycle, execution leases, durable ownership, and replayable cleanup.

    The central invariant may be:

    agent no longer visible
    ≠ agent terminated
    ≠ resources released
    ≠ launch slot reclaimed

    A subagent should remain represented in durable runtime state until it reaches a verified terminal state.

    A possible lifecycle record:

    {
    "schema_version": "subagent-lifecycle/v0.1",
    "dispatch_id": "dispatch-184",
    "trajectory_id": "repository-task-001",
    "parent_agent_id": "agent-root",
    "child_agent_id": "agent-review-04",
    "runtime_instance_id": "process-7712",
    "task_ref": "task:review-auth-module",
    "status": "RUNNING",
    "lease": {
    "owner": "agent-review-04",
    "expires_at": "2026-06-24T20:14:00Z",
    "last_renewed_at": "2026-06-24T20:09:00Z"
    },
    "resource_accounting": {
    "counts_toward_limit": true
    }
    }

    A useful lifecycle could be:

    REQUESTED
    → STARTING
    → RUNNING
    → COMPLETING
    → COMPLETED
    → TERMINATING
    → TERMINATED

    with explicit exceptional states:

    FAILED
    CANCELLED
    TIMED_OUT
    ORPHANED
    TERMINATION_FAILED
    UNKNOWN_RUNTIME_STATE

    The important boundary is:

    «"COMPLETED" describes task outcome, while "TERMINATED" describes runtime cleanup.»

    A child may finish its work but still require resource cleanup:

    task completed
    → result receipt stored
    → termination requested
    → runtime termination confirmed
    → slot released

    The launch counter should be derived from verified live runtime instances, not from stale parent-thread memory.

    A reconciliation process could compare:

    durable lifecycle registry
    ↔ runtime process registry
    ↔ parent-visible agent list
    ↔ resource-limit accounting

    and produce typed findings:

    {
    "child_agent_id": "agent-review-04",
    "registry_status": "RUNNING",
    "runtime_status": "NOT_FOUND",
    "parent_visibility": "MISSING",
    "classification": "ORPHANED_RECORD",
    "recommended_action": "RECONCILE_AND_RELEASE_SLOT"
    }

    The opposite case should also be detected:

    {
    "child_agent_id": "unknown",
    "runtime_instance_id": "process-8891",
    "registry_status": "NOT_FOUND",
    "runtime_status": "RUNNING",
    "classification": "ORPHANED_RUNTIME",
    "recommended_action": "QUARANTINE_OR_TERMINATE"
    }

    Manual lifecycle controls could operate on stable identities:

    • "list_subagents"
    • "inspect_subagent"
    • "request_cancel"
    • "terminate"
    • "force_terminate"
    • "reconcile"
    • "release_stale_slot"

    Each control should emit a durable receipt rather than only updating the TUI.

    For example:

    {
    "event_type": "SUBAGENT_TERMINATION",
    "child_agent_id": "agent-review-04",
    "requested_by": "local-user",
    "mode": "FORCE",
    "status": "SUCCEEDED",
    "runtime_instance_id": "process-7712",
    "slot_released": true
    }

    A deterministic conformance fixture could test:

    1. completed child remains visible until termination is confirmed;
    2. successful termination releases exactly one launch slot;
    3. repeated cleanup is idempotent;
    4. invisible but running runtime is classified as orphaned;
    5. stale registry entry with no runtime releases its slot safely;
    6. partial cleanup reports remaining agents rather than claiming success;
    7. force termination produces an explicit audit event;
    8. parent restart reconstructs the full child registry;
    9. orphaned child cannot later submit an accepted result without identity validation;
    10. resource exhaustion degrades gracefully instead of freezing the root session.

    Related LS work:

    Would a small vendor-neutral "SubagentLifecycleRecord", "TerminationReceipt", and reconciliation fixture help make orphan detection, cleanup, and launch-slot accounting machine-testable?

  10. linerking commented on Jul 4, 2026

    @linerking

    Adding one current user-facing data point from Codex Desktop around the same lifecycle-control gap.

    A user ended up with 100+ subagents shown in the sidebar after subagent-heavy work. From the model/tool surface available in the active thread, the agent could only see targeted lifecycle operations such as close_agent(target) and resume_agent(id). It could not enumerate all currently open/live/completed subagents, so it could not safely do the obvious cleanup loop:

    list agents -> inspect status -> close completed/inactive agents -> report remaining active agents
    

    This makes the current cleanup model fragile:

    • completed agents can remain open and continue to count toward the subagent/concurrency limit until closed;
    • the parent/model can only close agents whose ids survived in context;
    • after compaction or long sessions, those ids may be unavailable to the model;
    • the sidebar may keep accumulating child-agent/thread entries, which looks like "100+ subagents" to the user;
    • thread archive/sidebar cleanup is not equivalent to runtime close_agent slot/resource cleanup, so users do not know which action is safe.

    The missing primitive seems smaller than a full orchestration redesign:

    1. A model/user-facing list_agents or inspect_agents that returns all subagents for the current root/thread tree, including status and whether each counts toward the active limit.
    2. A safe bulk cleanup command such as close_completed_agents / close_inactive_agents that uses the same release path as close_agent.
    3. A bounded/error-returning close_all_agents or force_close_agent path for stale/interrupted agents, so cleanup cannot hang the parent turn indefinitely.
    4. A clear distinction in UI/API between:
      • runtime-close / slot release,
      • completed result/history retention,
      • sidebar archive/hide.

    Without this, the user can see the problem in the UI but neither the user nor the parent agent has a reliable supported way to enumerate and clean it up. The workaround becomes manual tracking of spawned ids or database/sidebar cleanup, which is not a safe lifecycle control.

  11. ChunqiGuo02 commented on Jul 16, 2026

    @ChunqiGuo02

    Additional Windows data point: persistent edges remain open while runtime sees no agents

    I observed the opposite state-divergence direction from #25426.

    Environment:

    • Windows x86_64
    • Parent session created by Codex Desktop / CLI 0.144.0-alpha.4
    • Reproduced and inspected with CLI 0.144.5
    • Multi-agent v1

    Observed behavior:

    1. A historical parent session had 18 subagent relationships stored as
      open in state_5.sqlite.
    2. All child threads had been inactive for approximately six days.
    3. Resuming the parent with Codex 0.144.5 and requesting closure of all
      remaining subagents reported that there were no subagents left to close.
    4. The database still contained all 18 relationships with status = 'open'.
    5. PRAGMA integrity_check returned ok, so this was not SQLite corruption.
    6. After backing up the database, changing only those stale relationships from
      open to closed resulted in:
      • zero open spawn edges,
      • database integrity still ok,
      • codex doctor: 0 warnings and 0 failures.

    This appears to be durable/runtime lifecycle divergence in the opposite
    direction from #25426:

    • durable thread_spawn_edges: open
    • runtime agent registry: no closeable/live agents

    Expected behavior:

    • Resume/startup reconciliation should detect open persistent edges whose
      runtime agents no longer exist.
    • Such agents should either be reconstructed as closeable agents or safely
      transitioned to a terminal state.
    • Users should not need to edit state_5.sqlite manually.

    I cannot confirm whether these persistent open rows were counted against
    agents.max_threads; I did not observe an explicit agent thread limit reached log for this case.

  12. safal207 commented on Jul 16, 2026

    @safal207

    Thanks for the additional data points. These reports make it clear that this is not only a stale-UI issue or a single "close_agent" bug, but a broader lifecycle state divergence problem.

    We now have evidence of several inconsistent states:

    • persistent edge is "closed", while the runtime agent still exists and continues to consume a slot;
    • persistent edge is "open", while the runtime no longer has a corresponding agent;
    • the runtime reports no live agent, while the Desktop sidebar still shows the subagent;
    • the parent agent loses subagent IDs after context compaction and cannot enumerate or safely clean them up.

    This suggests that "runtime registry", "thread_spawn_edges", and the UI/thread tree are being treated as separate sources of state without a reliable reconciliation step.

    A minimal safe fix would likely need:

    1. A bounded and idempotent "close_agent", with a timeout instead of an indefinite wait.
    2. Guaranteed release of the concurrency slot even when the child thread does not terminate cleanly.
    3. Startup/resume reconciliation for persistent "open" edges whose runtime agents no longer exist.
    4. A model/user-facing "list_agents" or "inspect_agents" operation that reports:
      • runtime status;
      • persistent status;
      • terminal state;
      • whether the agent currently counts toward the concurrency limit.
    5. Safe bulk operations such as "close_completed_agents" or "reconcile_agents".
    6. A clear UI distinction between:
      • stopping runtime execution;
      • releasing an agent slot;
      • retaining completed history;
      • archiving or hiding a sidebar entry.

    The key requirement is that users should never need to edit "state_5.sqlite" manually, and cleanup should never be able to block the parent turn indefinitely.

    A useful regression matrix would include both divergence directions:

    • persistent "closed" + runtime live;
    • persistent "open" + runtime missing;
    • interrupted child with "turn_aborted";
    • repeated "close_agent" calls;
    • resume after restart;
    • parent context compaction;
    • UI state after the runtime handle has disappeared.

    This issue appears to be the right umbrella for the lifecycle-control and reconciliation gap, while the narrower issues can document the individual failure paths.

  13. mihailangel939-glitch commented on Jul 28, 2026

    @mihailangel939-glitch

    I can reproduce a Windows desktop app variant of this orphaned-subagent lifecycle issue.

    Environment

    • Codex desktop app for Windows
    • Model shown in the affected chat: GPT-5.6 Sol, Ultra
    • Observed on July 28, 2026
    • Local project/workspace (no cloud task involved)

    What happened

    The Subagents panel showed 4 active and 87 completed. These four entries remained under Active with continuously increasing timers:

    • Stage13 portfolio contracts — 2h 36m
    • Stage12 options dto strict — 10h 44m
    • Stage12 docs — 11h 25m
    • Stage09 existing tests — 8d 2h 58m

    However, the live agent registry showed no child agents, and explicit interrupt attempts for the corresponding canonical task names all returned not_found:

    • stage13_portfolio_contracts
    • stage12_options_dto_strict
    • stage12_docs
    • stage09_existing_tests

    The entries disappeared only after fully exiting and restarting the Codex desktop app.

    Expected behavior

    • The app's Active counter should reflect the live agent registry.
    • Missing/completed agents should be reconciled automatically and moved out of Active.
    • Stale entries should not keep increasing their displayed runtime.
    • Stop-all / individual stop should either terminate a live agent or clearly identify a stale record.
    • The UI should make it clear whether an orphaned Active entry is still consuming usage limits.

    Impact

    The stale state made it look as if four agents—one for more than eight days—were continuously consuming usage. Because the backend no longer recognized them, they could not be stopped through the orchestration controls. Restarting the app was the only recovery that cleared the panel.

    I have a screenshot showing the four Active entries, their timers, and the 87 completed agents, but the connector used to post this comment cannot upload a local image attachment.

  14. mihailangel939-glitch commented on Jul 28, 2026

    @mihailangel939-glitch
    Image
  15. flatdemo1213 commented on Aug 6, 2026

    @flatdemo1213

    Adding a current Codex Desktop macOS data point that appears to match this lifecycle/reconciliation issue.

    Environment

    • Codex Desktop 26.730.61639 (6234)
    • macOS
    • Reproduces across multiple local workspaces

    Observed behavior

    • Subagents return their final handback and their child rollout reaches a normal task_complete event.
    • The Subagents panel still lists those agents under Active/Processing, with elapsed timers continuing for 6–8 days.
    • In the parent rollout, the affected agents retain started / interacted activity but do not receive a natural-completion terminal activity entry.
    • Manually opening an affected child thread causes Desktop to read/resume it and report latestTurnStatus=completed. After leaving that child thread, the panel moves it to Done and stops the timer.
    • In the same parent, entries that already appear under Done correspond to agents that received an explicit terminal/interrupted activity entry.

    Expected behavior

    A normal child task_complete should reconcile the child runtime/thread state with the parent activity state and Desktop UI automatically. The agent should move out of Active and its displayed timer should stop without requiring manual navigation into the child thread.

    Impact

    This makes completed work look as if it is still executing and leaves users unable to tell whether runtime resources or agent slots are still being consumed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingperformancesubagentIssues involving subagents or multi-agent features

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions