send_message: cross-session message is written to the target transcript and rendered, but never enters the receiving agent's context

Status Open
Reported on v2.1.229
Maintainer reply None cached
Activity 1 comment · opened Aug 18, 2026

Summary

A message delivered with the session-management send_message tool is written into the target session's transcript and rendered in the target's UI, but is not present in the receiving agent's context. Asked immediately afterwards whether it received anything, the receiving agent answers "No — nothing has come in" and correctly names the previous user turn, omitting the injected one.

The content is only recoverable by a human noticing the discrepancy and re-pasting the message body by hand.

Both sides believe they are fine: the sender gets a success return, and the receiver truthfully reports nothing arrived. There is no signal either party can use to detect the failure. The reporting user says this has happened multiple times.

Impact

send_message is documented for handing off context between sessions. In practice it cannot be relied on for that, because a successful return does not mean the receiving agent will ever read the message.

In our case a ~100-line engineering handover (merge instructions, a conflict-resolution warning, and a security defect) was sent, confirmed "sent", rendered in the target UI — and the target agent proceeded without any of it until the human pasted the whole thing in manually. Had the human not checked, the receiving session would have resolved a merge conflict the wrong way and silently reverted six pieces of prior work.

Steps to reproduce

  1. From session A, call send_message targeting session B with a distinctive body.
  2. Observe A's return value: Message sent to session <id> ("<title>").
  3. In session B's UI, observe the message rendered as a "Message from <A's title>" card containing the body.
  4. In session B, ask the agent: "did you receive a message?"
  5. Session B's agent replies that nothing has arrived, and names the user turn prior to the injected one as the most recent.

Observed on Claude Code 2.1.229 (claude-desktop entrypoint), Windows 11, both sessions local and non-remote.

What the transcript actually contains

Reading session B's transcript back shows the message is present, stored as a user turn wrapped in an envelope:

[user] <cross-session-message from="local_<sender-id>" name="<sender title>" encoded="1">
SENTINEL-BODY-HERE
</cross-session-message>

So the delivery and persistence half works. The gap is between the persisted transcript and the context assembled for the receiving agent's next turn.

I confirmed this twice: once for the original handover, and once with a fresh single-line sentinel sent to an idle throwaway session, read back via the transcript-reading tool.

Secondary observation: transcript search does not reliably find these

Full-text search across session transcripts did not match the sentinel body, despite the sentinel being present in that session's transcript when read directly. A different cross-session-message in another session was matched by the same search tool.

I have not isolated the cause — encoded="1" in the envelope suggests the stored form may differ from the rendered form, but index timing is an equally plausible explanation and I did not distinguish them.

This matters mainly because it removes the obvious workaround: a sender cannot audit its own delivery by searching for what it sent. A negative search result is not evidence of non-delivery.

What works correctly, for contrast

Sending to an archived session fails loudly and usefully:

Session <id> is archived; unarchive it first.

So the tool does surface failures it can detect. That makes the silent context omission more surprising, not less.

Suggested fixes, in order of preference

  1. Include the cross-session-message turn in the receiving agent's next-turn context. This is the actual bug; everything else is mitigation.
  2. Make the receiving agent's ignorance non-silent. If an injected turn is present in the transcript but outside the context window that produced the current answer, the agent should be able to say "there is a message I cannot read" rather than "nothing has come in". A confident denial is worse than an error, because it is indistinguishable from genuine non-delivery and prompts the sender to redo work.
  3. Make the sender's return value honest. Message sent currently confirms persistence, not delivery-to-agent. If the tool cannot guarantee the latter, say so in the return value so callers can fall back to a durable channel.
  4. Consider making these envelopes searchable, so delivery is auditable.

Note on the queued path

When the target session was mid-turn, send_message returned a different value:

Message queued for session <id> ("<title>"); it will be processed after the
in-flight turn finishes if that session stays healthy.

I could not confirm whether that message was ever delivered. I initially concluded it was lost, based on a transcript search that found nothing — but per the secondary observation above, that search is not a reliable absence test, so I am withdrawing the claim rather than reporting it as fact. Flagging it only because the "if that session stays healthy" caveat suggests the queued path has its own failure mode worth checking while the primary bug is being looked at.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗