Sub-agents do not reliably signal lifecycle state when crashing, hanging, or losing connection

Resolved 💬 2 comments Opened Mar 10, 2026 by CodingOX Closed May 10, 2026

What variant of Codex are you using?

CLI

What feature would you like to see?

A mechanism for sub-agents to prove liveness and for parent agents to detect unresponsive or crashed
sub-agents.

Currently, when a sub-agent is spawned via spawn_agent, the parent agent tracks its status through a
watch::Receiver<AgentStatus> channel. Status transitions occur only when the sub-agent actively emits
events (TurnStarted → Running, TurnComplete → Completed, Error → Errored, etc.).

However, there are three critical scenarios where no events are sent, leaving the parent agent unable
to detect the problem:

  1. Silent crash – The sub-agent process crashes without emitting ShutdownComplete or Error
  2. Hang/deadlock – The sub-agent process is alive but stuck (deadlock, infinite loop, blocked I/O)
  3. Connection loss – The sub-agent loses connectivity and cannot communicate

In all these cases, the sub-agent's status remains Running indefinitely. The parent agent has no way to
distinguish between "still working" and "dead/stuck".

Note: The spawn_agents_on_csv (Agent Jobs) feature already includes a max_runtime_seconds timeout for
exactly this purpose, but the standard spawn_agent tool has no equivalent protection.

Additional information

Current Implementation:

  • Status tracking uses tokio::sync::watch channel (codex-rs/core/src/codex.rs)
  • Parent subscribes via subscribe_status() (codex-rs/core/src/agent/control.rs)
  • Status only updates when send_event_raw() is called with status-changing events
  • No heartbeat or keepalive mechanism exists

Gap Analysis:

┌───────────────────┬─────────────────────────────┬─────────────────────────┐
│ Scenario │ Current Behavior │ Problem │
├───────────────────┼─────────────────────────────┼─────────────────────────┤
│ Normal completion │ TurnComplete → Completed │ ✅ Works │
├───────────────────┼─────────────────────────────┼─────────────────────────┤
│ Error │ Error event → Errored │ ✅ Works │
├───────────────────┼─────────────────────────────┼─────────────────────────┤
│ Explicit shutdown │ ShutdownComplete → Shutdown │ ✅ Works │
├───────────────────┼─────────────────────────────┼─────────────────────────┤
│ Silent crash │ No event │ ❌ Status stays Running │
├───────────────────┼─────────────────────────────┼─────────────────────────┤
│ Hang/deadlock │ No event │ ❌ Status stays Running │
├───────────────────┼─────────────────────────────┼─────────────────────────┤
│ Connection loss │ No event │ ❌ Status stays Running │
└───────────────────┴─────────────────────────────┴─────────────────────────┘

Suggested Solution:

Add an optional idle_timeout_seconds parameter to spawn_agent that monitors the sub-agent's activity.
If no status-changing events are received within the timeout window, the parent agent is notified (and
optionally the sub-agent is auto-shutdown).

// Example API
spawn_agent(
message: "Process this file",
idle_timeout_seconds: 300, // Optional: 5-minute idle timeout
)

This approach:

  • Maintains backward compatibility (timeout is optional)
  • Reuses existing watch channel infrastructure
  • Provides flexibility for tasks with varying expected durations
  • Aligns with the existing max_runtime_seconds pattern in Agent Jobs

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗