Cowork: device bridge websocket rotates every ~30 min ('rotating' in main.log, since ~2026-07-23 build) — cloud session surfaces it as full remote-devices MCP disconnect
Summary
Cloud Cowork sessions lose the entire remote-devices MCP server (~1000 tools deregistered mid-session) roughly twice per hour. Root cause per local logs: desktop app builds since ~2026-07-23 rotate the device-bridge websocket on a fixed ~27–33 min cadence, and the cloud session surfaces this designed, sub-second rotation as a hard MCP-server disconnect.
Related symptom reports: #77385, #76054, #78671 — this issue adds log forensics (the rotation line, measured cadence, drain budget, and the date the behavior first appears).
Environment
- Product: Claude desktop app (macOS, arm64) — Cowork cloud session + remote-devices bridge
- App version: 1.24012.9 (bundle updated 2026-07-24; rotation behavior first logged on the 2026-07-23 build)
- Host: MacBook Pro 2024, macOS, stable LAN — nothing changed host-side
- Frequency: ~100% of cloud sessions longer than ~30 minutes, daily since 2026-07-23
Root-cause evidence (~/Library/Logs/Claude/main.log)
2026-07-25 16:13:40 [info] [remote-tools-device] rotating (drain_wait_ms=0 inflight=0)
2026-07-25 16:13:40 [info] [remote-tools-device] reconnect #1 in 1000ms
2026-07-25 16:13:41 [info] [remote-tools-device] connecting wss://bridge.claudeusercontent.com/devices/<elided>/bridge
- 125
rotatingevents in the current log; zero before 2026-07-23 05:38 (log starts 2026-07-22 06:48) → the rotation shipped in the ~07-23 update. - Measured cadence on 2026-07-25 (19 consecutive intervals): 27.4–32.2 min, mean ~29.5 (06:51, 07:23, 07:53, 08:25, 08:54, 09:22, 09:52, 10:20, 10:51, 11:20, 11:49, 12:18, 12:49, 13:16, 13:47, 14:19, 14:47, 15:18, 15:46, 16:13 …).
- Drain budget: 123/125 rotations logged
drain_wait_ms=0 inflight=0; the two with in-flight requests showdrain_wait_ms=10177/11863→ in-flight calls get at most ~10–12s before the socket rotates out from under them (device tool calls legitimately run up to ~60s). - Occasionally the reconnect handshake stalls with backoff (
handshake timeout→reconnect #2/#3, e.g. 2026-07-24 01:43:49–01:44:23), turning a 1-second rotation into a multi-minute outage. - Rotation fires regardless of activity (fixed TTL) — keep-alive/heartbeat traffic does not prevent it.
The actual defect
The local reconnect completes in ~1 second, but the cloud session treats the rotation as a fatal MCP-server disconnect: all tools are deregistered, calls landing in the swap gap fail with "device … is not connected to the bridge", and the reconnected server is only re-announced at a later turn boundary. Agents routinely mis-diagnose this as a dead device/broken SSH and burn user time and tokens re-verifying healthy hosts.
Suggested fixes (any one helps)
- Make rotation transparent to the cloud session: hold/queue calls during the ~1s swap instead of propagating a disconnect.
- Raise the drain budget well above 10–12s (bridge tool calls run up to ~60s).
- Make-before-break: connect the new socket before closing the old one so tool registration never gaps.
- At minimum: re-announce the reconnected server immediately (not at the next user turn) and label the failure as a transient rotation, not device-not-connected.
Repro
Open any cloud Cowork session bound to a desktop device; issue device tool calls continuously for ~35 minutes; failures align exactly with a rotating line in the desktop main.log.
Current user-side workaround (for fellow sufferers)
The cadence is regular enough to schedule around: parse the last [remote-tools-device] rotating line from main.log, treat last + 27–34 min as the danger window, keep bridge calls short near it, and simply retry on the next turn after a drop — the bridge is already healthy again. Do not restart the app (it self-heals in ~1s; restarting is strictly worse).
4 Comments
Follow-up: the blast radius is narrower than it looks — cloud-side connectors are untouched by the rotation
Three days of living with this daily, and a data point that sharpens the diagnosis (and gives fellow sufferers a much better workaround than "wait it out").
Only the device-bridge MCP server drops. MCP connectors the cloud session reaches server-to-server keep working straight through a rotation.
Measured today (2026-07-28, same app build):
bridge-status.shpredicted the danger window as 11:43–11:50 from the lastrotatingline.create_tasks,update_tasks, twoadd_comment,get_task,delete_task). All six succeeded. Zero blips, zero retries.Desktop_Commander,ssh) in the same session are the only ones that ever fail, and they fail exactly on therotatinglines.Why this is worth adding to the issue
It narrows where the bug is. The cloud session's MCP layer is evidently fine at keeping a server registered across a transport hiccup when the transport is its own — the deregistration storm is specific to the device-bridge-proxied server. That's consistent with suggested fixes #1 (hold/queue during the ~1s swap) and #3 (make-before-break) being the right shape, and it argues the problem is not "MCP disconnects are handled harshly in general" but "the bridge propagates a designed rotation as a fatal disconnect."
It also means the ~1000-tool deregistration is doing avoidable collateral damage: a session that has both a device bridge and cloud connectors loses only a slice of its capability for ~1s, but the failure is presented as though the whole device is gone.
The practical upshot — the danger window stops being dead time
The workaround in the issue body was "keep bridge calls short near the window and retry after." Better version, now that we know the split:
In this session that turned the rotation from a ~1-minute stall into nothing at all — the window was spent doing Asana writes that were never at risk. It doesn't fix the bug, but it takes the sting out of it for anyone whose sessions mix both kinds of tool.
One caveat for the same audience: this only helps if you know which of your tools are device-proxied. Anything under the device-bridge namespace is at risk; anything the cloud reaches directly is not. Worth being explicit about, because the failure message ("device … is not connected to the bridge") reads like a whole-session problem and invites exactly the wrong response — restarting the desktop app, which is still strictly worse than waiting the ~1 second out.
Happy to pull further log forensics if any of this is useful.
Follow-up with a week of measured data, since this is still reproducing on 1.24012.9 (checked 2026-07-30) and I now have enough log to characterize it precisely.
369 rotation events in
main.logsince the 2026-07-23 build. A continuous 21-hour sample (Jul 29 18:01 → Jul 30 15:13) shows the cadence holds at 27.4–32.2 minutes around the clock, including overnight with zero user activity and no cloud session attached — so this is a fixed transport timer, not load- or activity-driven:Two details that may help whoever picks this up:
drain_wait_ms=0; two show it waiting (7914,701). So the rotation can drain — it just doesn't wait anywhere near the ~60s that device tool calls (file staging, shell-outs over the bridge) legitimately run. Any call in flight at rotation dies with "device is not connected to the bridge."handshake timeoutevents in the same log, including a cluster of three within 33 seconds (2026-07-26 06:38:58 / 06:39:14 / 06:39:31). During one such storm a cloud session lost allremote-devicestools for ~7 minutes straight.The compounding cloud-side problem remains as in the original report: the session interprets this scheduled rotation as a fatal MCP-server disconnect, deregisters ~1000 tools mid-turn, and only re-announces them at the next user-turn boundary.
Concrete ask, in order of preference: (a) treat the scheduled rotation as a transparent transport swap — keep the tool registrations alive through a reconnect grace window instead of deregistering; (b) honor a drain budget ≥ the max tool-call timeout (60s) before cutting the old socket; or (c) overlap old and new sockets during handover. Any one of the three would make this invisible.
For anyone else hitting this meanwhile: we've ended up building a full detach layer around it (fire jobs via a signed HTTPS mailbox, run detached on the device, collect results from a relay the bridge can't touch) so a rotation can no longer kill or block anything — but it's several hundred lines of workaround that any one of the fixes above would delete.
Still reproducing on 1.24012.11 (macOS arm64, updated 2026-08-04). Two-point probe after the update:
rotatingevents at 04:47:55 and 05:15:52 CT — 28.0 min apart, within the previously reported 27.4–32.2 min cadence, samedrain_wait_ms=0fingerprint. Timer resets on app restart; rotation still fires regardless of activity.Still reproducing on 1.30096.1 (macOS arm64, installed 2026-08-14 07:42:33 CT). Two rotations since, 33.0 min apart, same
drain_wait_ms=0 inflight=0fingerprint.I now have four weeks of continuous instrumentation, which is enough to characterize this properly — and to correct two things in my own original report.
Correction 1: onset is ~6 days earlier than I reported, and I named the wrong build
The original report claimed "zero
rotatingevents before 2026-07-23." That was wrong, and the error is instructive: I had read only the livemain.log, whose rotation boundary happened to land on 07-23. I mistook a log-rotation boundary for the onset of a socket-rotation bug.Reading all retained
main*.logmoves the first event six days earlier, to the morning of an app update:First rotation fires 29m00s after the app relaunches onto 1.22209.0.
Caveat, stated plainly: my retained logs begin 2026-07-16 15:24 and contain no
remote-tools-devicelines at all before that update, so I can't distinguish "feature absent" from "bridge not connected." 1.22209.0 is an upper bound on onset, not a proven first-affected build.Correction 2: app bundle mtime is not install time
Flagging this for anyone else measuring "has it stopped since the update," because it cost me a wrong answer this morning.
/Applications/Claude.apphad mtime 2026-08-13 21:34:34 — that is when the update finished downloading. The version actually switched at 2026-08-14 07:42:33, ten hours later. Anchoring on bundle mtime inflates the apparent clean-since-update window and can manufacture a false "looks fixed." The[updater] Version changed since last launchline is the reliable anchor.Per-build breakdown: 1,038 rotations, 8 builds, 27 days, zero clean days
Intervals in minutes, gaps >120 min excluded (app closed overnight):
| build | installed | rotations | n | min | mean | max |
|---|---|---:|---:|---:|---:|---:|
| 1.22209.0 | 2026-07-17 07:22:46 | 88 | 87 | 27.2 | 30.6 | 46.6 |
| 1.22209.3 | 2026-07-19 04:14:14 | 220 | 218 | 27.1 | 33.1 | 76.7 |
| 1.24012.9 | 2026-07-25 04:26:20 | 368 | 367 | 8.7 | 32.4 | 98.7 |
| 1.24012.11 | 2026-08-04 04:00:05 | 42 | 41 | 27.1 | 34.6 | 94.0 |
| 1.25927.0 | 2026-08-05 05:08:54 | 88 | 86 | 27.1 | 31.2 | 52.7 |
| 1.26832.0 | 2026-08-07 04:48:41 | 147 | 146 | 27.1 | 31.3 | 100.5 |
| 1.28929.0 | 2026-08-12 04:50:17 | 83 | 82 | 27.0 | 36.3 | 92.5 |
| 1.30096.1 | 2026-08-14 07:42:33 | 2 | 1 | 33.0 | 33.0 | 33.0 |
(The 1.22209.3 row also covers 1.24012.1 — that install predates my retained
updaterlines, so I can't split it out.)Aggregate: 1,038 events, 2026-07-17 → 2026-08-14, n=1,033 intervals, min 8.7 / median 30.2 / mean 32.6 / max 100.5.
The floor is the signal
1,035 of 1,037 intervals are ≥ 27.0 min, and the per-build minimum sits at 27.0–27.2 min on seven of the eight builds. Only two intervals in four weeks fall below it (25.0 min on 07-26, 8.7 min on 07-27); I found no
Starting appline between either pair, though I only retain 19 such lines, so I can't rule out a restart whose record has since rotated away.A hard floor at 27 minutes that reproduces to within 0.2 min across a 1.22 → 1.30 minor-version jump and eight releases is not network flakiness and not connection aging. It reads as a configured rotation timer that no release in four weeks has touched.
This is not macOS-specific
Worth surfacing across tickets: #77385 is labelled
platform:macos, but it now carries two independent Windows reproductions at the same ~30 min cadence, with close code 1006 (@arigsbee and @eduncanjr, both 2026-08-13). Same cadence, different OS, different network. Combined with the interval floor above, that points at bridge-side rotation policy rather than anything platform-local or network-local.Live blind-prediction repro, captured while writing this comment
The cadence is regular enough to forecast. At 09:07 CT today I predicted from the log that the next rotation would land in the 09:11–09:17 window. It fired at 09:16:45, 31.8 min after its predecessor — and it killed the tool call I was using to write this comment, which is a fairly on-the-nose demonstration of the failure mode:
Blast radius from the cloud side, measured in the same second:
The device this session is bound to is not connected to the bridge.device_bash,device_list_dir,device_stage_files,device_commit_files) re-registered first, with the full 1,011 restored only afterward.That staged recovery looks like the same "partial bridge state" @thekhairulakbar describes in #77385 (some tools succeeding while others fail simultaneously), which is further reason to think these two tickets are one bug.
What would help
The rotation itself is cheap — the local reconnect completes in ~1s. The damage is entirely that the cloud session treats it as a fatal MCP disconnect and deregisters the device toolset mid-session. If the rotation is deliberate (credential refresh, LB draining), a graceful handover that keeps the tool registry alive across the swap would close this without changing the cadence at all.
Happy to supply the raw 1,038-event list, the per-build parser, or logs from any window that would help.