Dispatch session collisions cause message loss and transport failures

Status Closed — not planned
Maintainer reply None cached
Activity 13 comments · opened Mar 28, 2026 · closed May 7, 2026

Bug: Dispatch session collisions cause message loss and transport failures

Environment

  • App: Claude Desktop (macOS)
  • Date observed: 2026-03-27
  • Log source: ~/Library/Logs/Claude/main.log

Summary

When using Dispatch mode (Cowork), the sessions-bridge encounters repeated session collisions where multiple cloud session IDs (CSEs) attempt to bind to the same local Dispatch parent session. This creates a rapid displacement loop that prevents messages from being delivered, causes transport initialization failures, and results in user messages being silently dropped or routed incorrectly.

The user-visible symptom is that replies typed into a Dispatch thread are either lost entirely or "passed off" into a different Copilot chat session that then fails to respond.

Reproduction

  1. Open a Cowork/Dispatch session with several child tasks spawned.
  2. Send follow-up messages in the Dispatch thread while child sessions are active.
  3. Observe that the reply either never arrives or surfaces in the wrong session context.

Exact trigger conditions are unclear, but the collision loop began immediately after a prior session query completed with isError=true.

Error Details

1. Session collision loop (11 collisions in ~2 minutes)

Starting at 17:03:58, pairs of CSE sessions repeatedly displace each other on the same local parent (local_ditto_6cf1aa12-...):

[error] [sessions-bridge] Session collision: cse_016UUj8Mj7XJAUsMEAWAw5hW is displacing cse_01DPeFSKvZkbvD3heTsfDWzN (both bound to local_ditto_6cf1aa12-..., displaced pendingTurns=0)
[error] [sessions-bridge] Session collision: cse_01DPeFSKvZkbvD3heTsfDWzN is displacing cse_016UUj8Mj7XJAUsMEAWAw5hW (both bound to local_ditto_6cf1aa12-..., displaced pendingTurns=0)

This pattern repeated with at least 6 different CSE IDs cycling against each other (cse_01DPeF..., cse_016UUj..., cse_01NLWFp..., cse_015zc5..., cse_01Dbz..., cse_01Qtic...).

2. Transport initialization failure during collision

A collision triggered a deleteBridgeSession which wiped the local parent while a new CSE transport was still attaching:

[warn] [sessions-bridge] Failed to post final result for cse_01DPeFSKvZkbvD3heTsfDWzN; server DELETE may 409: write() before transport initialized
[error] [sessions-bridge] Failed to connect transport for session cse_01DPeFSKvZkbvD3heTsfDWzN: transport closed during attachBridgeSession

The sequence was: collision detected -> deleteBridgeSession wipes local parent -> OAuth cache cleared -> fresh OAuth exchange started -> but transport was already closed -> attachBridgeSession fails.

3. Stale pendingTurns

Throughout the session, there are recurring warnings about stale pending turns suggesting the bridge loses track of in-flight work:

[warn] [sessions-bridge] Stale pendingTurns detected for session cse_01A2W7aptb5XBXZXwLsrc7Bx (pendingTurns=2), resetting counter and keeping transport open
[warn] [sessions-bridge] Stale pendingTurns detected for session cse_01PJkqGu6XntvdWGT7r4uG2v (pendingTurns=1), resetting counter and reconnecting transport

4. User-reported symptoms in-log

The logs capture the user's own messages describing the failure:

  • 17:25:31 — "Something is going on with your messaging system in dispatch mode. Your replies are being sent via s..."
  • 18:15:21 — "You're having problems responding in this dispatch thread. I can see you kicked off a claude code se..."
  • 18:21:07 — "I'm typing in a dispatch thread, but its literally just passing it off into a copilot chat and faili..."

Root Cause Hypothesis

The sessions-bridge poll API is returning duplicate or recycled work items for the same local Dispatch parent. When a new CSE arrives while an existing CSE is already bound, the bridge displaces the old one. But the displaced session re-appears in the next poll cycle, creating a ping-pong loop. This loop:

  1. Prevents any single transport from staying connected long enough to relay messages.
  2. Can trigger deleteBridgeSession which wipes the local parent and all its OAuth state, causing a cascade of re-authentication and further transport failures.
  3. Leaves pendingTurns in a stale state so the bridge doesn't know whether work completed.

A possible contributing factor is the isError=true completion at 17:03:33 on the prior session (cse_01A2W7apt...), which may have left the server-side session in an ambiguous state that caused it to re-dispatch work.

Expected Behavior

  • Only one CSE should be bound to a local Dispatch parent at a time.
  • If the server issues a new CSE for the same parent, the old CSE should be cleanly retired (not re-polled).
  • User messages sent during Dispatch should be reliably routed to the active session and never dropped.

Actual Behavior

  • Multiple CSEs fight over the same local parent in a collision loop.
  • The transport is destroyed and re-created repeatedly, sometimes failing to initialize.
  • User messages are lost or misrouted to the wrong session type.

Suggested Investigation Areas

  • sessions-bridge poll deduplication: Why does the poll API return work for sessions that were just displaced? Is there a missing acknowledgment or session-state fence?
  • deleteBridgeSession race condition: The wipe-and-recreate path during a collision doesn't appear to guard against concurrent transport attachment.
  • OAuth cache invalidation during collision: The collision path calls clearing token cache which forces a fresh exchange; under rapid collision this could amplify latency and failure rates.
  • Stale pendingTurns recovery: The "reset counter" path doesn't appear to notify the server, potentially causing the server to re-dispatch the same work.

Log File

Full logs available at ~/Library/Logs/Claude/main.log on the reporter's machine. Key line ranges: 15972–16296 (collision loop), 18170–18200 (user complaint context).

View original on GitHub ↗

13 Comments

github-actions[bot] · 5 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/40032
  2. https://github.com/anthropics/claude-code/issues/37473
  3. https://github.com/anthropics/claude-code/issues/38049

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

Vgccorner · 5 months ago

Yeah this happens to me too

jkdavies · 5 months ago

Still reproducing after full uninstall/reinstall of Claude Desktop. The same session collision loop is happening on the fresh install (2026-03-28 ~21:45-21:48 UTC):

[error] [sessions-bridge] Failed to connect transport for session cse_01PJkqGu6XntvdWGT7r4uG2v: Request failed with status code 404
[error] [sessions-bridge] Session collision: cse_01YAYXMajbLXoy2XpCV78SDi is displacing cse_01QticnqBeE9qLLLrERHHTtX
)both bound to local_ditto_6cf1aa12-..., displaced pendingTurns=3)
[error] [sessions-bridge] Session collision: cse_01YAYXMajbLXoy2XpCV78SDi is displacing cse_01QticnqBeE9qLLLrERHHTtX
[error] [sessions-bridge] Session collision: cse_01QticnqBeE9qLLLrERHHTtX is displacing cse_01YAYXMajbLXoy2XpCV78SDi

New findings vs. original report:

  1. 404 on transport connect - cse_01PJkqGu6XntvdWGT7r4uG2v (a session from the pre-reinstall run) is still being polled by the server and now returns a 404 when the client tries to attach a transport.
  2. Invalid lifecycle transition - [warn] Invalid transition archived → stopping for the Dispatch parent session, suggesting the client is trying to operate on an already-archived session.
  3. Server-side root cause confirmed - The local parent ID (local_ditto_6cf1aa12-...) persisted across the reinstall because it is tied to the org ID, not the local app installation. The server continues to dispatch stale work items for sessions that will never be serviceable, causing the collision loop to resume immediately on any fresh install.

This is a server-side bug - no client-side workaround will fix it.

jkdavies · 5 months ago

Update 3: Collision loop is resolved, but Dispatch UI still not rendering responses.

After deleting the stale local_ditto_6cf1aa12-... session files from /~/Library/Application Support/Claude/local-agent-mode-sessions/.../agent/, the session collision loop stopped. The new Dispatch session (cse_01YAYXMajbLXoy2XpCV78SDi) connects cleanly and completes queries with isError=false.

However, the Dispatch thread UI still does not render any responses. The user can send messages (they show as sent with a "Read" checkmark), but no reply ever appears in the thread.

From claude.ai-web.log:

[error] Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'includes')
[error] [REACT_QUERY_CLIENT] QueryClient error: Error: Not found

And from main.log:

[localAgentModeSessionManager] Bridge session: disabling renderer-dependent tools

This looks like the UI renderer is trying to look up data associated with the old Dispatch parent session that no longer exists, hitting "Not found" in the React query cache, and silently failing to render.

So there are actually two bugs here:

  1. [Server-side, original report] Session collision loop caused by duplicate work item dispatch. Workaround: delete the local local_ditto_* session files.
  2. [Client-side, new] Dispatch UI does not render responses even when the backend completes successfully. The React query client throws "Not found" and there is an uncaught TypeError on .includes. There is no way to start a fresh Dispatch thread from the UI to work around this.
jkdavies · 5 months ago

Additional Finding: Dispatch agent spawned with UI presentation tools explicitly disallowed

After further investigation into the Dispatch UI responsiveness issue, I discovered a separate bug in the Dispatch spawn configuration that prevents the agent from rendering results in the Cowork UI — even when the backend successfully completes the task.

The Problem

In cowork_vm_node.log, the Dispatch agent's spawn command includes mcp__cowork__present_files in both --allowedTools and --disallowedTools:

--allowedTools ...,mcp__cowork__present_files,...
--disallowedTools AskUserQuestion,mcp__cowork__allow_cowork_file_delete,mcp__cowork__present_files,mcp__cowork__launch_code_session,mcp__cowork__create_artifact,mcp__cowork__update_artifact

Since --disallowedTools takes precedence, the following tools are effectively blocked for Dispatch sessions:

  • mcp__cowork__present_files
  • mcp__cowork__create_artifact
  • mcp__cowork__update_artifact

These are the exact tools the agent needs to render file outputs and artifacts back into the Cowork UI.

Observed Behavior

  1. Dispatched a "Create HTML on Desktop" task
  2. The agent successfully created hello.html (confirmed via file system and Desktop panel in UI)
  3. When the agent attempted to present the file back to the user, the present_files tool call was blocked
  4. The agent itself acknowledged: "I created the file but didn't present it to you through the UI" and confirmed the tool was blocked

Impact

This is a separate issue from the session collision loop documented above. Even after resolving the session collisions (via "Disconnect all and retry" or clearing bridge-state.json), the Dispatch agent still cannot present results to the user because the presentation tools are disallowed in the spawn configuration.

Environment

  • Claude Desktop macOS, Max plan
  • claude-code-vm SDK version 2.1.85
  • Opus 4.6 model
jkdavies · 5 months ago

Diagnostic Update: Backend succeeds, UI fails to render responses

Further investigation via Cowork session analyzing main.log in real-time confirms the issue is a client-side UI rendering bug, not a backend failure.

Evidence

After a full quit + relaunch + sign-out/sign-in cycle:

  1. Registration conflict cleared — The 409 registration conflict ("A Cowork agent is already registered on [hostname]") that appeared on previous launches did not recur after the restart.
  1. All MCP servers connected — 14 servers with tools loaded, including the dispatch server (5 tools). No connection errors.
  1. Dispatch turns complete successfully — When sending messages in the Dispatch UI, main.log shows:

``
[sessions-bridge] Received user message for session cse_016pGmAszALq5imfL17yssmc: "You're back!..."
[Lifecycle] Session local_ditto_*: idle → initializing → running
[Result] Turn succeeded for session local_ditto_*
[sessions-bridge] Query completed for session cse_016pGmAszALq5imfL17yssmc (pendingTurns=0, isError=false)
[CycleHealth] Healthy cycle: { cycle_health: 'healthy', had_first_response: true, seconds_to_outcome: 12 }
``

  1. But the Dispatch UI shows nothing — Despite the backend reporting had_first_response: true and isError=false, no response renders in the Dispatch panel. The user sees their sent message and silence.
  1. Pattern is intermittent — The first message after restart ("lets work on something together") got a visible response. The second message ("You're back! I've been troubleshooting...") completed successfully in the backend but rendered nothing in the UI. Subsequent messages also complete backend-side but show nothing.

Key Distinction

This is not the session collision issue from the original report. The 409 conflict was absent on this launch. The sessions-bridge connected cleanly, the Dispatch parent agent processed every message to completion, and CycleHealth reported all cycles as healthy. The disconnect is between the sessions-bridge reporting Query completed and the Dispatch UI panel actually rendering the response.

Secondary issues observed

  • coworkd.log: DNS resolution failures (Cannot read TLS response from mitm'd server dial tcp: lookup api.anthropic.com on 127.0.0.53:53: connection refused) during VM startup — resolved after recovery
  • swift.log: Notifications are denied (Notifications are not allowed for this application) — cosmetic but contributes to apparent unresponsiveness
  • main.log at 22:53:20: Sentry error ProcessTransport is not ready for writing — may indicate transport instability between app and VM

Environment

  • Claude Desktop macOS, Max plan
  • claude-code-vm SDK version 2.1.85
  • Opus 4.6 model
  • macOS (Justins-MacBook-Pro.local)
jkdavies · 5 months ago

Follow-up: Dispatch agent IS generating full responses — they vanish before rendering

Examined the Dispatch parent session's audit.jsonl transcript directly to see what the agent thinks it responded with. The agent is generating complete, substantive responses to every message — they're just not making it to the UI.

What the agent actually generated (from audit.jsonl)

For each user message sent in the Dispatch UI, the parent agent (local_ditto_*) produced full responses:

  1. "lets work on something together" → Agent responded: "I'm game! What's on your mind? Could be anything from farm marketing decisions, a document or presentation, some research, code work, organizing your week — whatever you need."
  1. Long message about troubleshooting history + GitHub issue #40070 → Agent read the uploaded claudecleanreinstall.md, fetched the GitHub issue via WebFetch, and wrote a multi-paragraph analysis summarizing all three bugs identified in the issue. Full tool use chain: ReadBashWebFetch → detailed response.
  1. "ahh, bummer. Looks like you've gone silent again" → Agent responded: "Still here! Sorry about the delay..." — it didn't even know the user couldn't see its previous responses.
  1. "what is causing you to fail to respond here in the dispatch ui?" → Agent investigated, read the uploaded file, fetched the GitHub issue, and wrote a thorough diagnostic summary.

The gap

Every one of these responses shows up in audit.jsonl as completed assistant turns. The main.log confirms each with [Result] Turn succeeded, Query completed (pendingTurns=0, isError=false), and [CycleHealth] Healthy cycle: { had_first_response: true }.

The responses are being generated, serialized to the audit log, and reported as delivered to the sessions-bridge. They disappear somewhere between sessions-bridge reporting Query completed and the Dispatch UI panel rendering them. Only the very first response after a fresh app restart rendered visibly.

Implication

This is definitively a client-side rendering bug in the Dispatch UI panel, not a backend, transport, or agent issue. The full agent pipeline (message receipt → processing → tool use → response generation → session completion) works correctly every time.

jkdavies · 5 months ago

Root Cause Found: Dispatch agent stops using SendUserMessage after first turn

Traced the exact mechanism by examining the Dispatch parent session's audit.jsonl transcript line by line.

The mechanism

The Dispatch parent agent (local_ditto_*) has SendUserMessage in its --allowedTools and uses it correctly for its very first response:

Line 2: TOOL_USE: SendUserMessage -> {"message": "Hey Justin! I'm ready. What are you thinking?"}

This first message rendered successfully in the Dispatch UI.

For every subsequent user message, the agent responds with plain text blocks instead of SendUserMessage tool calls:

Line 13: TEXT: "I'm game! What's on your mind?..."          ← NOT rendered
Line 19: TEXT: "Ugh, that's frustrating..."                  ← NOT rendered  
Line 25: TEXT: "Still here! Sorry about the delay..."        ← NOT rendered
Line 31: TEXT: "Good question — let me dig into..."          ← NOT rendered (then used Read, Bash, WebFetch tools)
Line 40: TEXT: "Okay, I read through your GitHub issue..."   ← NOT rendered
Line 45: TEXT: "I'm here! What do you want to work on?"      ← NOT rendered
Line 50: TEXT: "Loud and clear! What do you want to tackle?" ← NOT rendered

The agent generated thoughtful, complete responses — including multi-tool investigation chains — but never routed them through SendUserMessage, so they never reached the Dispatch UI. The sessions-bridge reports Query completed (isError=false) because the turn did complete from the backend's perspective.

Why it happens

The SendUserMessage tool is available (--allowedTools includes it), so this appears to be a model/system-prompt behavior issue: after the first turn, the agent falls back to plain text assistant responses instead of calling SendUserMessage. The Dispatch UI apparently only renders content delivered via SendUserMessage, not raw assistant text blocks.

The present_files conflict (separate issue, confirmed still present after update)

Also confirmed that mcp__cowork__present_files still appears in both --allowedTools and --disallowedTools in the spawn config after today's app update (SDK 2.1.85). The disallowed list wins, blocking file/artifact presentation:

--allowedTools ...,mcp__cowork__present_files,...
--disallowedTools AskUserQuestion,mcp__cowork__allow_cowork_file_delete,mcp__cowork__present_files,mcp__cowork__launch_code_session,mcp__cowork__create_artifact,mcp__cowork__update_artifact

Additionally, main.log shows Bridge session: disabling renderer-dependent tools every time the Dispatch parent initializes — this is the code path adding present_files et al. to the disallowed list without checking for conflicts with the allowed list.

Summary

There are two distinct bugs causing Dispatch UI silence:

  1. Primary (text responses): The Dispatch agent stops using SendUserMessage after its first turn, falling back to plain assistant text that the UI doesn't display. Fix would be either enforcing SendUserMessage usage in the Dispatch system prompt, or having the UI render raw assistant text blocks.
  1. Secondary (file/artifact responses): present_files, create_artifact, and update_artifact are blocked by the renderer-dependent tools disabling code, preventing file and artifact presentation even when the agent tries to use them.

No user-facing settings can work around either issue.

jkdavies · 5 months ago

Workaround Found + Full Root Cause Chain

Workaround (confirmed working)

Telling the Dispatch agent directly to use SendUserMessage restores visible responses:

"I'm facing an issue where when responses you generate are relayed to me in plain text, the Dispatch UI isn't rendering it properly so I never see it, even though you think you've responded. So for now, always respond using the SendUserMessage tool."

The agent acknowledged the bug and committed to using SendUserMessage going forward. Responses are now rendering in the Dispatch UI.

Full root cause chain

Traced by examining cowork_vm_node.log spawn configs and the Dispatch parent's audit.jsonl transcript:

  1. SendUserMessage is only available to the Dispatch parent (Bridge session) — confirmed by comparing --allowedTools across all 49 spawned sessions in this log. Child sessions (Cowork, Dispatch tasks) never have it. This is by design.
  1. The Dispatch parent used SendUserMessage exactly once — on its very first response. Every subsequent response (7+ messages) was generated as plain assistant text blocks, which the Dispatch UI silently drops.
  1. The --resume flag locks in the bad pattern — the Dispatch parent always spawns with --resume <same-cli-session-id>, replaying the full transcript. Once the model responded with plain text once, every subsequent resume loads that history, and the model mimics its own prior behavior. The pattern is self-reinforcing.
  1. Reinstalling doesn't fix it — because local-agent-mode-sessions/ (containing the transcript) and bridge-state.json (binding to the cloud session) survive or get re-bound to the same session state. The accumulated plain-text response history persists across reinstalls.
  1. The present_files conflict is separate but still presentmcp__cowork__present_files appears in both --allowedTools and --disallowedTools in the spawn config, with disallowed winning. This blocks file/artifact presentation but is not the cause of text message silence.

Why this may be user-specific

The core bugs (renderer-dependent tools disabling, present_files conflict) appear to be universal in the codebase. But the symptom — persistent Dispatch silence — likely requires a trigger: the model needs to respond with plain text instead of SendUserMessage just once, and then the --resume flag makes it permanent. Different users might not hit this depending on their initial interaction pattern, conversation length, or model sampling.

Suggested fixes (in order of impact)

  1. Enforce SendUserMessage in the Dispatch parent system prompt — the system prompt should mandate that all user-facing responses go through SendUserMessage, not plain text
  2. Have the Dispatch UI render plain assistant text blocks — as a fallback, so responses aren't silently dropped
  3. Fix the present_files allowed/disallowed conflict — remove it from the disallowed list for Bridge sessions, or check for conflicts before applying the renderer-dependent tools blocklist
  4. Consider not using --resume indefinitely — or periodically resetting the Dispatch parent transcript to prevent behavioral drift
marlvinvu · 5 months ago

Your debugging skill is extremely high! Your debug work is incredibly valuable.

marlvinvu · 5 months ago

A user in #40281 found one root cause for Dispatch responses not rendering: the missing --brief CLI flag prevents SendUserMessage from being registered. However, your issue also involves a separate problem (TypeError on dispatch_agent_name being null / session collision loop) which may be an independent bug. My Claude is not entirely sure the --brief fix alone would resolve your case. You should check issue #40281 for the full details.

github-actions[bot] · 3 months ago

Closing for now — inactive for too long. Please open a new issue if this is still relevant.

github-actions[bot] · 2 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.