Sub-agents do not reliably signal lifecycle state when crashing, hanging, or losing connection
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:
- Silent crash – The sub-agent process crashes without emitting ShutdownComplete or Error
- Hang/deadlock – The sub-agent process is alive but stuck (deadlock, infinite loop, blocked I/O)
- 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
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗