Repository navigation
send_message_to_thread reports handler error even when delegation is delivered #29886
Description
Summary
send_message_to_thread can return No handler registered for tool: send_message_to_thread even though the target Codex thread actually receives the message as a codex_delegation and starts processing it.
This makes the sender side think delivery failed when it did not.
Environment
- Codex app: 26.616.81150
- Bundled Codex CLI:
codex-cli 0.142.0 - Surface: Codex desktop app, local thread coordination tools
Observed behavior
When sending follow-up prompts from one Codex thread to other local Codex threads:
- Some calls returned success with a
threadId. - Other calls returned:
No handler registered for tool: send_message_to_thread
However, after calling read_thread on those target threads, the latest target-thread input was present as a codex_delegation with:
- the expected source thread id
- the expected prompt content
- target thread status changing to active / in progress
Example source thread:
019efa60-ec1f-7df2-83f8-117fbaf17288
Affected target examples:
019eb9dc-449d-7ac1-a467-82087206d53c
019eb9dc-6332-79e3-92f8-bc3eefb911e5
019eb494-6e05-7d12-9ae9-753efac95105
019ed108-2f13-7c00-a528-1ad31f99fa7f
019ef324-d21a-7b32-8f3c-4fa1a72895a6
A later read_thread call confirmed the delegation had actually been delivered to each of those threads.
Expected behavior
If delivery succeeds, send_message_to_thread should return success, ideally with a stable delivery receipt or message id.
If the tool cannot determine final delivery state, it should return an explicit unknown / pending delivery state rather than a generic handler error.
If delivery succeeds but an acknowledgement handler fails, the result should distinguish that case, e.g. delivered_but_ack_failed.
Actual behavior
The call returns a handler error that looks like delivery failure, while the target thread actually receives and processes the delegation.
Impact
This breaks multi-thread coordination reliability. An agent may:
- incorrectly mark a message as not delivered
- ask the user to manually forward a message that was already delivered
- retry and create duplicate tasks
- write inaccurate coordination logs
Temporary workaround
After send_message_to_thread reports a handler error, call read_thread on the target thread and verify whether the latest input is a matching codex_delegation with the expected sourceThreadId and content before retrying or marking delivery failed.
Suggested fix
Separate message delivery status from local acknowledgement / handler failure. If a post-delivery acknowledgement handler fails, surface it as an acknowledgement error while preserving a successful delivery result.
Activity
ogreguro commentedon Aug 8, 2026
Observed again in Codex Desktop on 2026-08-08 (America/New_York):
send_message_to_threadreturnedNo handler registered for tool: send_message_to_threadfor a live handoff, while the same immutable handoff could still appear delivered in the UI after repeated submissions.Impact: an automation cannot distinguish a false-negative transport error from non-delivery, so safe coordination must treat UI results as unverified and wait for an independent acknowledgement. This is especially disruptive for multi-agent task routing.
Requested behavior: a stable delivery acknowledgement or an idempotency key/queryable status for thread-message delivery, and no handler error after a message has been accepted. No proprietary task text, logs, credentials, or source files are included here.
ogreguro commentedon Aug 8, 2026
Automated sanitized Codex Desktop function-fault report.
ogreguro commentedon Aug 8, 2026
Automated sanitized Codex Desktop function-fault report.
rtol5 commentedon Sep 30, 2026
Our current SSH-backed sessions lack the native sender, so we have not independently reproduced this native handler error. However, this delivery ambiguity materially affects an existing-peer release-coordination workflow.
In our local adapter, acceptance, observed input and substantive completion are separate states. We distinguish failures before attempting delivery from failures after attempting it. A durable caller request ID returns the prior receipt for identical repeated arguments; changed arguments are rejected, and pending/uncertain requests are not automatically resent.
We tested duplicate suppression across separate MCP processes and concurrent requests. We still verify the request-specific input/result; activity or absence from a truncated read is insufficient.
It would help if the native sender accepted an idempotency key and returned a request/message identifier that could be reconciled after an error. Otherwise, a sender cannot safely choose between resending potentially duplicated work and leaving a possibly undelivered request unresolved.