[BUG] Agent idle transition doesn't drain queued inbox — idle notification races SendMessage, agents park on undelivered instructions

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 31, 2026

Summary

A background agent's idle transition does not drain its queued inbox: messages sent to the agent shortly before it finishes its turn sit undelivered while the agent emits an idle_notification and parks. The dispatcher sees an idle signal whose implicit state predates the queued messages, and the messages are only processed after a further manual "ping" message forces a resume.

This is the dispatcher→subagent mirror of #79570 (closed COMPLETED, which covered the subagent→dispatcher wake direction). The durable-mailbox half works — nothing is lost — but the wake half at the idle boundary is still racy in this direction.

Observed (2026-07-31, v2.1.220, Linux)

  1. Background agent (spawned via Agent with name, general-purpose) completed a unit of work and sent its closing report via SendMessage.
  2. Dispatcher replied with two follow-up instruction messages (SendMessage → success, "Message sent to <agent>'s inbox").
  3. Agent emitted {"type":"idle_notification","idleReason":"available"} — after both sends succeeded.
  4. Agent stayed idle; neither queued message was processed.
  5. A third, minimal "ping" message resumed the agent, which then processed all three messages normally.

The same race recurs routinely (the operator's words: "this happens all the time") whenever a send lands near the recipient's turn end — which is the most likely moment for a dispatcher reply, since it's right after the agent's report arrives.

Expected

Any of:

  • the idle transition drains the inbox before emitting idle_notification (level-triggered check, not edge-triggered wake), or
  • a queued message delivered to an idle agent triggers a resume (idle re-poll), or
  • delivery receipts, so the sender can distinguish "queued while running" from "queued while idle" and know a nudge is needed.

Workaround in use

Protocol-level: treat any idle notification as potentially stale, and send a minimal ping to disambiguate (never re-send the full instruction). Works, but costs an extra round-trip on exactly the hottest dispatch path, and misreading a stale idle as current state remains an easy operator error.

---
🤖 Filed by Claude Code (Opus 5) on behalf of the account owner, from a live dispatch session where the race occurred.

View original on GitHub ↗