Skip to content

MultiAgentV2: send_message at a sampling boundary can leave a live subagent running but unscheduled #32756

Open

Description

@Lady-Lin

Environment

  • Surface: Codex Desktop / app-server, MultiAgentV2
  • Rollout-recorded CLI version: 0.144.1
  • Current local CLI version: 0.144.3
  • Subscription: Pro
  • Model: gpt-5.6-sol, maximum reasoning effort
  • Platform: Linux 7.0.0-27-generic x86_64 GNU/Linux

What issue are you seeing?

A queue-only send_message delivered near a model sampling boundary can leave a live subagent reported as running, but with no subsequent turn scheduled.

This was observed twice in one redacted Codex Desktop MultiAgentV2 workflow:

Attempt Completed child tool outputs before the status message Error events Delivery metadata Final outcome
Full implementation task 12 0 trigger_turn=false No later child tool/final event; parent eventually interrupted it
Narrowed implementation task 8 0 trigger_turn=false Same behavior; parent eventually interrupted it

Before each status message, the child was actively reading files and designing tests. The parent then used send_message to ask for the current phase without intending to interrupt the work. In each child rollout, the message was appended immediately after a reasoning response with:

{"type":"inter_agent_communication_metadata","payload":{"trigger_turn":false}}

After that event, the child produced no more tools, messages, or final response. Repeated wait_agent calls timed out, while list_agents continued to report the child as running.

No tool error, stream error, rate-limit event, shell hang, or application exception occurred. Raw rollouts are not attached because they contain private workspace paths and task content.

What steps can reproduce the bug?

The timing appears to matter.

  1. Enable MultiAgentV2 in Codex Desktop/app-server.
  2. Spawn a no-history child with a multi-step task that performs several tool calls before writing files:
    {
      "task_name": "implementation_probe",
      "fork_turns": "none",
      "message": "Inspect the relevant interfaces, write a failing test, implement the change, verify it, and report."
    }
  3. Wait until the child has completed several read-only tool calls and is sampling after a tool output.
  4. Ask for a status update using queue-only messaging:
    {
      "target": "/root/implementation_probe",
      "message": "Report your current phase and any blocker."
    }
  5. Inspect the child rollout. The incoming communication is recorded with trigger_turn=false at the sampling boundary.
  6. Call wait_agent and list_agents repeatedly.
  7. Observe that no new child turn is scheduled, wait_agent times out, and the child remains reported as running.
  8. Repeat with a smaller task. The same event sequence was observed.

What is the expected behavior?

A queue-only message should not strand an already-live task.

At least one of these should happen:

  • the child continues its original task loop and consumes the queued message at a later safe boundary;
  • the runtime schedules the next natural child turn even though the message itself is queue-only;
  • or the agent is reported as idle/pending-mail rather than running, with an actionable hint to use followup_task.

It would also help if list_agents exposed fields such as turn_active, pending_mailbox_items, and last_activity_at, so a controller can distinguish active sampling from a logically open but unscheduled task. A wait_agent timeout should remain a mailbox timeout, but the returned status could make this distinction explicit.

Additional information

The queue-only/trigger-turn distinction is intentional in current source: send_message uses QueueOnly, while followup_task uses TriggerTurn:

use crate::tools::context::FunctionToolOutput;
use codex_protocol::protocol::InterAgentCommunication;
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum MessageDeliveryMode {
QueueOnly,
TriggerTurn,
}
impl MessageDeliveryMode {
/// Returns whether the produced communication should start a turn immediately.
fn apply(self, communication: InterAgentCommunication) -> InterAgentCommunication {
match self {
Self::QueueOnly => InterAgentCommunication {
trigger_turn: false,
..communication
},
Self::TriggerTurn => InterAgentCommunication {
trigger_turn: true,
..communication
},
}

Related but distinct: #32353 reports a completed agent with pending queue-only mail pinning a residency slot and causing AgentLimitReached. This report does not involve capacity exhaustion or an already-completed child. It concerns a live task that remains shown as running after queue-only delivery but receives no later turn.

The report is intentionally sanitized: no repository name, workspace path, session/thread ID, original task text, local username, or private rollout payload is included.

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    app-serverIssues involving app server protocol or interfaces
    subagentIssues involving subagents or multi-agent features
    on Jul 13, 2026
  2. github-actions commented on Jul 13, 2026

    @github-actions
    Contributor

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

    Powered by Codex Action

  3. Lady-Lin commented on Jul 13, 2026

    @Lady-Lin
    Author

    Companion Superpowers workflow-guidance report: obra/superpowers#1979. That issue covers controller guidance for mailbox waits and explicit BLOCKED handling; this Codex issue remains focused on MultiAgentV2 turn scheduling and liveness observability.

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

    appIssues related to the Codex desktop appapp-serverIssues involving app server protocol or interfacesbugSomething isn't workingsubagentIssues 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