Claude Desktop on Linux freezes over a 27-byte reply

Claude Desktop on Linux hangs every couple of days because its Code tab never reads the 27-byte reply to a presence ping. Each ignored reply keeps a file descriptor open in Chromium’s renderer until the renderer runs into its limit of 1024. A developer found the cause after two months of debugging and wrote it up on Dfb on November 13. If your Linux install goes blank or stops reacting to clicks after a day or more of uptime, restarting the app gets you a fresh renderer. The fix the author proposes is one line in Anthropic’s frontend, and for now the author patches it in on top of the official build.
How it shows up
The freeze takes one of two forms: the window drops to skeleton placeholders, or it stays drawn and ignores every click. The app’s main.log only repeats “Main webview is unresponsive, will kill and reload” every 20 to 40 seconds, and by default the renderer’s own output goes to /dev/null. Once the author sent that output to the journal, a clearer sign showed up: 17,241 TransferBuffer::Initialize() failed lines within 8 seconds. Other deaths were logged as a clean exit with code 0. The app treats that as intentional and does not reload the window.
A crash has a side effect on the whole machine. systemd-coredump tries to save the renderer, and a Chromium renderer’s dump is 1.5 TiB, almost all of it address space reserved up front. Most of that is V8’s sandbox, the range the JS engine sets aside to contain corrupted pointers, and the renderer’s seccomp policy blocks the call that would keep it out of core dumps. On the author’s laptop the dump wrote 92.4 GiB in five minutes, pushed the load average to 46 and then timed out without leaving a file.
Anthropic’s first-party Linux build has shipped as a beta .deb since June 30, per the community claude-desktop-debian project on GitHub. The author’s copy runs on Electron 44, which is Chrome 152.
Why the descriptors pile up
The leaking request is the presence clear. When the window is hidden or idle, the Code tab tells the server this client has left the session: it sends a POST with keepalive: true and drops the result with .catch(()=>{}). The regular presence request does await r.json(). Only the clear skips it, and its reply is just {"refresh_after_seconds":0}.
Chromium hands every response body to the renderer through a 2 MiB shared-memory region, and each region holds one descriptor. Normally Blink drains the body into memory and the pipe closes, within 250 ms in the author’s repro. Blink skips that step for Cache-Control: no-store responses, and the presence replies took that path. So an unread body keeps its region and its descriptor until something reads or cancels it. Garbage collection does not free them either: in a repro where all 800 Response objects were collected, 836 descriptors stayed open.
The renderer’s soft descriptor limit is 1024, while the browser process raises its own to 524,288. A fresh renderer picked up 45 of these regions an hour. The hung one held 942 after 35 hours, and 938 of them contained only the presence reply. At the limit, the kernel cannot pass a descriptor over the IPC socket. On Chrome 152, Chromium’s IPC layer accepts the message without it and waits for it forever, and every later message on that channel queues behind it, including the reply the renderer’s main thread was blocked on.
The app also bundles Datadog’s browser SDK, which held 801 of those replies in a set capped at 1,000 requests. The author blamed it at first and then ruled it out: the repro above leaks just the same when nothing keeps the Response objects.
Fire-and-forget fetch
The same leak applies to any Chromium or Electron app that fires fetch() without reading the body when the server answers with no-store. The fix the author suggests is to cancel the body with .then(r => r.body?.cancel()), or to send the ping with navigator.sendBeacon. With the cancel in place, the Electron 44.9.3 repro finished all 1,100 requests at 38 descriptors. Without it, the page stopped at “fetched 900” with the renderer sitting at 1,020 to 1,024 descriptors. Firefox 159 headless completed all 1,100 with or without keepalive.
Chrome 154 made Chromium’s IPC fail more predictably when descriptors run out, but in the author’s tests that only changes how the page dies. On Electron 45 (Chrome 156) and the Electron 46 alpha (Chrome 159), keepalive requests still end with the renderer exiting with code 0 and no error reaching the page. Without keepalive on the Electron 46 alpha, the next fetch() never settles.
Chromium has tracked the leak since 2024 as a P3 bug. The owner of that bug wrote: “If you can read the Response, but choose not to, I think this is working as intended. The browser cannot know that you won’t read the response in future, so it has to hold on to it.” A later comment on the same bug proposes buffering bodies under about 512 KB once they have fully arrived. The author’s own report, which asked for fetch() to reject with a TypeError when it can’t get a descriptor, was closed the same day as a duplicate.
On Anthropic’s side, a bot closed the author’s GitHub issue with the label invalid and the message “Closing for now — this doesn’t appear to be about Claude Code.” The request in question comes from the app’s Code tab, which is Claude Code.
Related stories
- Claude Code sends file hashes to Anthropic even on DeepSeek
- Claude desktop teardown: MCP servers run outside the VM
- Claude limit meter may charge 1.4x more in US mornings
- Claude Opus 5.5 finished Baldur’s Gate 3 alone in 38.9 hours
- Claude Code hackathon in Japan arms 250 builders with Max 5x
- Claude’s cipher project writes off the gold bar codes
Comments
No comments yet. Be the first.
Join the conversation
Sign in to leave a comment. It appears after moderation.
