{"slug": "why-claude-desktop-on-linux-freezes-a-27-byte-telemetry-reply-and-an-fd-leak", "title": "Why Claude Desktop on Linux Freezes: A 27-Byte Telemetry Reply and an FD Leak", "summary": "Developer deafbeef traced freezes in Claude Desktop on Linux to unread `Cache-Control: no-store` fetch responses left open in Chromium Mojo data pipes, causing file-descriptor exhaustion, according to a public write-up posted November 14–15, 2026 on blog.dfb.sh. The triggering traffic is a Claude Code presence-clear POST sent with `keepalive: true` and `.catch(() => {})` that never reads the server's 27-byte `{\"refresh_after_seconds\":0}` reply; the author's hung-process dump showed 942 two-megabyte shared-memory regions, 938 holding only that JSON, alongside repeated `TransferBuffer::Initialize() failed` errors. A minimal repro without retaining Response objects still leaked 836 descriptors, and the author reports the vendor ticket is disputed; the post is not an official Anthropic publication.", "body_md": "**Claude Desktop on Linux can look healthy in logs, then freeze with placeholder skeletons or a stuck typing animation.** Developer **deafbeef** spent two months patching a local install, captured a **1.5 TiB sparse renderer core dump** (mostly reserved V8 sandbox address space, not RAM), and traced the hang to **hundreds of two-megabyte shared-memory response pipes** that each held the same **27-byte JSON** payload: `{\"refresh_after_seconds\":0}`.\n\nThe write-up lives at [blog.dfb.sh](https://blog.dfb.sh/blog/claude-desktop-freeze/?ref=explainx). It is not an official Anthropic post. It is one of the most detailed **Chromium-in-Electron** forensic stories tied to a **frontier model desktop app** in 2026, and it matters because **coding agents** increasingly ship inside **desktop shells** with **always-on telemetry**.\n\n## TL;DR\n\n| Question | Answer | \n|---|---|\n| Symptom | UI skeletons, dead clicks, streaming replies frozen mid-token | \n| Root mechanism (author) | **Unread**`Cache-Control: no-store` bodies stay open in** Mojo pipes** →**FD exhaustion** | \n| Triggering traffic | **Claude Code**`presence`** clear** POST with` keepalive: true` ,`.catch(() => {})` | \n| Response payload | **27 bytes** JSON refresh hint | \n| Electron / Chromium | Claude Desktop **Electron 44** ≈**Chrome 152** in the traced build | \n| Datadog SDK | Bundled; **not** the sole holder of pipes per later repro | \n| Fix status | Public **Nov 14–15, 2026** post; vendor ticket disputed | \n\n## Why empty logs mislead\n\nMain-process logs showed only **“Main webview is unresponsive, will kill and reload”** on a loop. Reload often failed. The renderer sometimes exited with **`FATAL: Crashing because hung`**, but stderr went to `/dev/null` until the author redirected it.\n\nThat pattern matches other [Claude Desktop reliability threads](https://explainx.ai/blog/claude-deleted-law-professor-syllabus-lecture-notes-baude-dropbox-2026) where users blame the model when the **shell** is the bottleneck. Here the shell lost the ability to talk to its **browser process** because IPC backlog met **descriptor starvation**.\n\nA rounded tank with green drops falling steadily from a small crack, echoing a slow descriptor leak\n\n## The presence request that nobody reads\n\nOn the Code tab, Claude Desktop sends **presence telemetry** so the server knows a client is active. When the window is hidden or idle, it sends a **clear**:\n\n```\nPOST /v1/code/channels/<id>/client/presence\n{ \"client_id\": \"...\", \"clear\": true }\n```\n\nThe author reports this variant uses:\n\n``` js\nfetch(url, { method: \"POST\", headers, body, keepalive: true }).catch(() => {})\n```\n\nSo the client **never awaits** or **reads** the response body. The regular presence heartbeat **does** call `await r.json()`, per the post. Only the **clear** path is fire-and-forget.\n\nThe server’s reply is tiny but not empty: **`{\"refresh_after_seconds\":0}`** — **27 bytes**.\n\n## Chromium, `no-store`, and why garbage collection is not enough\n\nWhen Blink hands a response body to the renderer, it normally wraps it in **`BufferingBytesConsumer`** so chunks drain quickly. **` fetch` manager** skips that wrapper when the response carries **`Cache-Control: no-store`** (or when integrity checking is enabled).\n\nFor unread **no-store** bodies, data stays in the **Mojo data pipe**. Each pipe uses a **2 MiB** shared-memory region and consumes **one file descriptor** in the renderer.\n\nTwo pipeline endpoints joined by a green adapter, echoing how a no-store fetch keeps a Chromium data pipe open\n\nThe author’s hung dump showed **942** such regions, **938** containing only the refresh JSON. Meanwhile **`TransferBuffer::Initialize() failed`** spammed the journal — classic **FD limit** pressure.\n\nInitial suspicion fell on **Datadog’s browser SDK**, which records completed fetches in a **Set** capped at **1000** entries. Unread **no-store** responses never get **resource timing** events, so they linger. A later **minimal repro without retaining Response objects** still leaked **836 descriptors** after **800** requests, proving **Chromium does not close pipes on GC**.\n\nThat nuance matters for security teams: you cannot fix this class of bug only by **dropping telemetry vendor retention**. The **fetch caller** must **consume or cancel** the body, or the **browser** must release the pipe on object destruction.\n\n## Timeline the author measured\n\n| Phase | Observation | \n|---|---|\n| Days 1–11 | Random renderer death; capped core dumps useless | \n| Day 13 | **Clean exit** treated as intentional → blank UI until manual restart | \n| Day 21+ | Verbose Chromium logging; GPU buffer failures mixed in | \n| **35-hour uptime session** | Streaming reply frozen at **“6s · 246 tokens”** ; manual`gcore` while hung | \n\nA fresh renderer accumulated **~45** leaked SHM regions **per hour** from presence clears alone in the author’s test. Extrapolation explains multi-day uptimes crossing **hundreds** of pipes without a restart.\n\nA protected key inside a capsule beside a closed folder, echoing kernel file descriptors the renderer never releases\n\n## Electron memory dumps are not “1.5 TB of RAM”\n\nReaders on Hacker News often misread **sparse core files**. Chrome **152** reserves about **1 TiB** for the **V8 pointer sandbox** plus large **guard regions** so corrupted indices cannot escape. `gcore` reports **virtual size**, not resident pages. The author’s Rust parser used **`SEEK_HOLE` / `SEEK_DATA`** to search only mapped regions in **under a second**.\n\nThat technique is reusable for any **Chromium hang** where you cannot attach a debugger live.\n\n## What developers can do now\n\n**Mitigations (application side):**\n\n1. **Restart Claude Desktop** on a schedule if you keep week-long sessions on Linux.\n2. **Watch FD usage** : sudden**`TransferBuffer`** errors in journald around Claude processes.\n3. **Patch locally** if you maintain a fork: read or**`response.body.cancel()`** on presence clear, even when JSON is useless.\n4. **Raise `ulimit -n`** only as a band-aid; pipes still cost kernel bookkeeping.\n\n**Mitigations (platform side):**\n\n- Track **Electron upgrades** — the author notes newer Chrome builds may change pipe lifecycle (verify per release).\n- For internal Electron apps, audit **`keepalive` + no-store** combinations the same way you audit**memory leaks** .\n\n**Security posture:** This is a **denial-of-use** bug, not remote code execution. It still matters in **SOC workflows** where analysts leave Claude Desktop open across shifts. Our [desktop restriction guide](https://explainx.ai/blog/claude-desktop-restrict-access-sandbox-guide-2026) covers containment patterns; this post adds **reliability** pressure on the same surface.\n\nFor coding-agent harness design, compare [Claude Code CPU fixes](https://explainx.ai/blog/claude-code-cli-cpu-fix-bun-gc-idle-august-2026) and [Cursor incident traces](https://explainx.ai/blog/cursor-fireworks-security-incident-agent-traces-november-2026): desktop agents generate **continuous side traffic**; ignoring 27-byte replies is cheap until it is not.\n\n## What Chromium and Anthropic reportedly said\n\nThe author filed upstream. **Chromium** reportedly responded that behavior **matches spec intent**: unread **no-store** bodies should not be buffered in memory indefinitely, so the pipe stays open until consumed — consistent with the [Fetch living standard](https://fetch.spec.whatwg.org/?ref=explainx#fetching) guidance on suspending downloads.\n\nThe write-up also cites [Chromium issue 342779913](https://issues.chromium.org/issues/342779913?ref=explainx): upstream relanded a fix in **Chrome 154** so Mojo fails more predictably under FD exhaustion instead of hanging silently. Claude Desktop’s traced build sits on **Electron 44 / Chrome 152**, so Linux users may not get that behavior until the shell upgrades.\n\nAn **Anthropic** ticket apparently auto-closed as **not Claude Code**, despite the stack trace originating in the **Code tab presence client**. Whether that categorization changes after the public post is unknown.\n\nBuilders should treat vendor triage labels as **process noise** when the reproduction crosses **Electron + bundled observability SDKs + feature-specific fetch**.\n\n## Minimal lesson for agent shipping teams\n\nIf your product sends **heartbeat POSTs** from an embedded webview:\n\n- **Always drain or cancel** bodies, even for “meaningless” JSON.\n- Do not assume **`.catch` on fetch** releases kernel resources.\n- Load-test **FD counts** , not just**heap snapshots** .\n- Separate **telemetry SDK retention** from**browser networking contracts** .\n\nThe same lesson applies to [MCP-connected desktops](https://explainx.ai/blog/what-is-mcp-model-context-protocol-guide) and [Bedrock-attributed IDE spend](https://explainx.ai/blog/amazon-bedrock-ai-coding-tools-per-user-cost-iam-identity-center-2026): the billing line item is visible; the **descriptor leak** is not, until the UI dies mid-task.\n\n## What developers are saying\n\nThe [Hacker News thread](https://news.ycombinator.com/item?id=50499446&ref=explainx) was thin at crawl time (**5 points**, **1 comment**), so there is little independent reproduction yet. When more Linux users confirm FD growth on `lsof -p <renderer>`, add numbers here — the author invited minimal repro without Claude binaries.\n\n## What people are asking about the freeze\n\n**Is this the same bug as high CPU in Claude Code CLI?** No. explainx.ai covered [**Bun GC idle CPU**](https://explainx.ai/blog/claude-code-cli-cpu-fix-bun-gc-idle-august-2026) as a separate runtime issue. The deafbeef trace is **renderer networking state**, not model inference cost.\n\n**Does restarting fix it permanently?** Restart clears FD tables until presence clears accumulate again. For multi-day sessions, periodic restarts or a body-drain patch are the practical options the author describes.\n\n**Can I see it without a core dump?** The post suggests watching **open FD count** on the renderer PID and journal lines like **`TransferBuffer::Initialize() failed`**. If those climb while idle, you are likely in the same failure class even before the UI freezes.\n\n**Should enterprises block Claude Desktop on Linux?** That is a risk call. The failure is **local availability**, not remote exploitation. Security teams may still care because **frozen agent sessions** strand **uncommitted work** and encourage unsafe **copy-paste recovery**. Pair monitoring with the [sandbox restrictions guide](https://explainx.ai/blog/claude-desktop-restrict-access-sandbox-guide-2026).\n\n## How `fetch` and `keepalive` interact here\n\nThe author highlights **`keepalive: true`** on the clear request. Keepalive lets the browser finish a request after navigation or teardown, which is useful for **beacon-style telemetry**. Combined with **unread bodies**, it extends pipe lifetime across task boundaries.\n\nFor application authors, the safe pattern is:\n\n``` js\nconst response = await fetch(url, { method: \"POST\", headers, body, keepalive: true });\nawait response.body?.cancel();\n```\n\nEven when JSON is worthless, **cancellation** releases the pipe. Alternatively **`await response.arrayBuffer()`** on a 27-byte payload is cheap insurance.\n\nChromium’s **no-store** branch exists so sensitive bodies do not linger in memory. The trade-off is **kernel resources** until consumption. Agent products that poll every few minutes multiply that trade-off across **workdays**.\n\n## Monkey patches the author shipped before the root cause\n\nBefore the core dump, deafbeef layered local fixes on Claude Desktop: cap **systemd-coredump** size, reload the webview on **clean-exit** renderer deaths, and stream **Chromium verbose logs** into journald. Each patch reduced downtime but masked the underlying **descriptor curve**. That journey is familiar to anyone who maintained **Electron chat clients** during the early Slack desktop era: symptomatic reload loops until someone counts **handles**.\n\nIf you maintain an internal fork, consider **feature-flagging presence clear** during investigations, or route clears through a **single shared worker** that enforces **body cancellation**. Either approach beats asking users to tolerate minute-long UI freezes during **long agent runs**.\n\n## Related reading\n\n*Electron version numbers, presence endpoint paths, and Datadog SDK behavior reflect deafbeef’s public post accessed 15 November 2026. Claude Desktop builds change frequently — confirm against your installed version.*", "url": "https://wpnews.pro/news/why-claude-desktop-on-linux-freezes-a-27-byte-telemetry-reply-and-an-fd-leak", "canonical_source": "https://explainx.ai/blog/claude-desktop-linux-freeze-telemetry-fd-leak-deafbeef-2026?utm_source=rss&utm_medium=feed&utm_campaign=blog", "published_at": "2026-11-15 00:00:00+00:00", "updated_at": "2026-11-15 12:52:31.320698+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-agents", "ai-tools", "developer-tools"], "entities": ["deafbeef", "Anthropic", "Claude Desktop", "Claude Code", "Electron", "Chromium", "Datadog"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/why-claude-desktop-on-linux-freezes-a-27-byte-telemetry-reply-and-an-fd-leak", "markdown": "https://wpnews.pro/news/why-claude-desktop-on-linux-freezes-a-27-byte-telemetry-reply-and-an-fd-leak.md", "text": "https://wpnews.pro/news/why-claude-desktop-on-linux-freezes-a-27-byte-telemetry-reply-and-an-fd-leak.txt", "jsonld": "https://wpnews.pro/news/why-claude-desktop-on-linux-freezes-a-27-byte-telemetry-reply-and-an-fd-leak.jsonld"}}