Repository navigation
[Bug]: Claude Desktop picker mode breaks claude.ai chat (sends time out, picker 502s) since the merged chat/Cowork front end; picker off fixes it #6511
Description
Client or integration
Claude Desktop (macOS), first-party mode with picker mode on
Area
Proxy / Claude Desktop picker (claude-intercept egress on 127.0.0.1:10201)
Summary
With picker mode active, Claude Desktop pins all of its traffic to the opencodex egress proxy ([egress-proxy] pinned to fixed proxy at 127.0.0.1:10201; OS proxy settings ignored). Since 2026-10-02 ~12:00 local, the chat side of Desktop no longer works: a sent message spins and eventually fails with "message could not be sent", and sessions fail with "Timed out loading session". The Code tab keeps working, and opencodex models still appear in the Code tab picker.
The breakage lines up with a new claude.ai front-end build loading in Desktop (the merged chat/Cowork experience, served remotely, so no app update was needed). The first failed send was 16 minutes after that build first loaded. Picker mode had worked with chat since 2026-09-26.
Turning the picker off (ocx claude desktop picker off, restart Desktop) removes the egress pin, and chat works with zero errors. Turning it back on brings the failures back. Upgrading 2.72.0 → 2.76.0 did not fix it.
Reproduction
- macOS, opencodex in first-party mode with
ocx claude desktop picker on(status:effective: true, reason: active, trust: trusted). - Claude Desktop 2.19675.0, on an account that has the merged chat/Cowork front end.
- Open the chat side (not the Code tab) and send any message.
- The message spins, then "message could not be sent". Existing chats fail to load with "Timed out loading session. Check your connection and try again."
- Run
ocx claude desktop picker off, quit and reopen Desktop. Chat works.
Version
- opencodex 2.72.0 (when it started), still reproduces on 2.76.0
- Claude Desktop 2.19675.0, bundled Claude Code 2.1.286
Operating system
macOS 26.6.2 (25G83), arm64
Provider and model
Not model-specific. The failing traffic is Desktop's own claude.ai traffic (chat UI, session loads, feature flags), not routed model requests.
Logs or error output
Desktop claude.ai-web.log (renderer), starting 2026-10-02:
12:16:15 [error] [useSendChannelMessage] send failed [object DOMException]
12:16:57 [warn] [REACT_QUERY_CLIENT] QueryClient error: {"name":"TimeoutError","message":"Timed out loading session. Check your connection and try again."}
Desktop main.log: main-process fetches to claude.ai time out continuously while the picker is active (349 on 10-02, 517 on 10-03 so far):
[warn] [my-access] fetch failed { error: DOMException [TimeoutError]: The operation was aborted due to timeout }
[error] [growthbook] network error: DOMException [TimeoutError]
[error] Failed to check allowlist status: DOMException [TimeoutError]
[warn] [RemotePluginManager] Failed to fetch enabled state; defaulting to all-enabled This operation was aborted
opencodex stdout during the same window: the picker answers some claude.ai requests with 502 (2.72.0: 590 GET + 368 POST 502s in the log; 2.76.0 since this morning: 15 GET + 3 POST 502s):
[claude-picker] picker GET other 502
[claude-picker] picker POST other 502
Timeline (local time):
- 2026-09-26: picker mode applied. Chat works through the egress proxy.
- 2026-10-02 ~12:00: new claude.ai web build (asset
c219730b0) first loads in Desktop. - 2026-10-02 12:16: first failed send. Failures continue all day.
- 2026-10-02 22:16: Desktop restarted with the picker off: zero timeouts or failed sends afterwards.
- 2026-10-03 00:13: opencodex auto-updated to 2.76.0, picker back on. Failures resume (latest 08:55 on 10-03).
Plain curl -x http://127.0.0.1:10201 https://claude.ai/ returns quickly with the same status as a direct request, so the listener is up. The failures look specific to the requests the new front end makes.
Redacted configuration
"claudeCode": { "enabled": true, "desktopMode": "first-party", "desktopNativeModels": true, "intercept": { "picker": true } }Desktop applied profile (Claude-3p/configLibrary, "opencodex-picker"): {"egressProxyUrl":"http://127.0.0.1:10201"}
Checks
- I searched existing issues and PRs (nothing open about Desktop chat failing under picker mode; fix: integrate reviewed provider, client, and desktop recovery train #6487 touches picker cleanup and catalog bounds, not passthrough).
- I removed secrets, tokens and personal data.
Activity
Ingwannu commentedon Oct 4, 2026
Related #6551 now has independently reproduced offline listener evidence: at 76fcad4 the 14 local TLS tests pass, including 24 KiB cookies, binary/SSE streaming and bounded overflow responses. This establishes the parser/header compatibility correction, not that it caused your merged-chat/Cowork failure: no captured Desktop header-size evidence or native macOS acceptance was collected here. Please keep this issue open pending that correlation. A useful redacted follow-up is exact OpenCodex/Desktop versions and whether the same send/session-loading failure recurs after the fix is available; do not post cookies, authorization headers, bodies or account data. I have not toggled the picker or restarted a live Desktop session.
fix(claude): preserve bounded large headers in picker relay (#6551)
lidge-jun commentedon Oct 4, 2026
The bounded picker-header correction has landed on dev via #6551, commit df3dd3f. Large ordinary request/response session headers are allowed up to the explicit limit; overflow, body/cookie preservation, streaming, upgrades and TLS behavior have focused and hosted regression evidence.
Keeping this issue open: the synthetic parser reproduction does not establish that header size caused the reported merged-chat/Cowork failure in the actual Desktop app. Native symptom correlation and end-to-end acceptance on the affected client remain outstanding. No installed profile, keychain trust or network setup was modified by this integration.
rhomat27 commentedon Oct 4, 2026
Tested the #6551 fix on the affected machine. It removes the 502s, but chat still does not load.
Setup
src/claude/intercept/picker-listener.tschange from df3dd3f applied (thePICKER_MAX_HEADER_BYTESserver and upstream limits plus the overflow log). Nothing else changed.[egress-proxy] pinned to fixed proxy at 127.0.0.1:10201.What changed
upstream:headers-too-largewas never logged.What did not change
Timed out loading session. Check your connection and try again.6 times andsignal timed out5 times.Failed to check allowlist status,Failed to fetch blocklist,Failed to check can_install,[growthbook] network error,[RemotePluginManager] Failed to fetch enabled state ... aborted,[SkillsPlugin] Skills request timed out.Observations that may help
GET other 101(WebSocket upgrade) lines in total, and that count has not changed since at least Oct 2 evening, when chat was already broken. Nothing has upgraded since this restart either. The renderer's failing call is[useSendChannelMessage] send failed, so the new front end may depend on a long-lived channel that doesn't make it through the relay. This is an inference from the logs; I have no packet capture.I'm leaving the picker off for daily use and can re-test a build on request.
Ingwannu commentedon Oct 4, 2026
This separates the two symptoms: your single-patch retest removes the picker 502s, but the send/session-loading failure remains and disappears with the picker off. #6551 is therefore not end-to-end resolution of this issue; it stays open.
One detail from current dev
a6bc608: the ordinarypicker METHOD other STATUSline is emitted when response headers are written, not when the response body finishes. Those 60 lines and the open-socket count cannot by themselves distinguish a normal long-lived stream from a request stuck waiting for upstream headers. Likewise, an unchanged historical 101 count does not yet prove the failing request uses WebSocket.If you can safely provide one representative failed session-load request from existing Network/diagnostic information, please include only: method and pathname (remove query/fragment and redact IDs), whether HTTP response headers arrived before timeout, and whether an Upgrade was requested/accepted (yes/no and status only). A response content type, if available, would distinguish SSE from ordinary HTTP. No full HAR, cookies, authorization headers, bodies, account data, packet capture or repeated daily picker toggling is needed. I have not reproduced the actual macOS client or established a relay fix, and have not changed any live profile/service.
rhomat27 commentedon Oct 4, 2026
Captured with temporary local logging in the picker listener and the CONNECT proxy. The logs recorded method, redacted path, whether headers arrived and the content type. No query strings, headers, cookies or bodies were logged. Run: Claude Desktop 2.19675.0 started 13:28:25 CDT with the picker on, opencodex 2.76.0 plus the #6551 header change, observed for about 4 minutes.
Every request that reached the picker got headers back. 80 requests started, 80 received response headers (all 2xx), and none hit a 15-second no-headers timer. No
Upgradewas requested or accepted; the upgrade handler never fired.The long-lived requests are SSE streams that stay open:
GET /v1/code/channels/:id/messages/stream: 200,text/event-stream, headers in about 210–300 ms. Six of these opened in the first minute and none closed./v1/code/sessions,/v1/code/sessions/:id,/v1/code/channels/:id/timeline,/v1/code/sessions/:id/events,/api/organizations/....The requests that time out never reach the picker. Over the same window Desktop logged main-process timeouts (
[growthbook] network error,Failed to check can_install,[my-access] fetch failed,[RemotePluginManager] ... aborted) and renderersignal timed outerrors. The picker log has nostartline for any of them. Only 9CONNECT claude.ai:443tunnels were opened in total.Likely cause: the picker listener only offers
http/1.1(ALPNProtocols: ["http/1.1"]), so each open SSE stream holds a whole connection. Chromium allows 6 HTTP/1.1 connections per host, so once the merged front end has 6messages/streamsubscriptions open, every other claude.ai request from the same Electron session waits for a free connection and times out. A direct connection to claude.ai negotiates HTTP/2, where those streams share one connection, which matches the picker-off case working. It also explains why the fault appeared when the new front end started opening per-channel streams, not after an opencodex change.If that holds, the fix is HTTP/2 on the picker's client-facing side (ALPN
h2with an h2-capable relay), or otherwise keeping Desktop from multiplexing claude.ai streams over HTTP/1.1. The picker is off again on this machine and the temporary logging has been removed.rhomat27 commentedon Oct 4, 2026
Answers to the four questions, plus a same-session re-test. All times are CDT on Oct 4. No query strings, IDs, headers, cookies or bodies are included.
A failed session-load request, in the fields you asked for
Timed out loading session,signal timed out, and main-processgrowthbook/can_install/my-access/RemotePluginManager) never reached the picker listener. With per-request logging on, none of them produced astartline, so I have no method or pathname for them from the relay side.GET /v1/code/channels/:id/messages/stream, 200text/event-stream, with headers in about 210–300 ms. Six opened in the first minute and none closed.Same-session re-test (Desktop 2.19675.0 started 13:28, not restarted afterwards)
127.0.0.1:10201, and newclaude.aiCONNECTs were blind-tunnelled. Chat recovered and a Sonnet message sent. The picker logged nothing for that send.10201rose from 10 to 46). Those were intercepted, and the timeouts resumed at 13:38:36. The picker logged 2 requests between 13:37:53 and 13:40:32.In one app process, chat works while
claude.aireaches Anthropic through a blind tunnel and jams once its connections terminate at the HTTP/1.1-only picker listener. That matches the per-host connection limit described in my previous comment.Ingwannu commentedon Oct 4, 2026
Your two follow-ups answer the diagnostic questions; no further picker toggling or full HAR is needed. The important new distinction is that the timed-out requests never enter the listener, while every observed request receives headers and the long-lived responses are SSE, not upgrades.
At current dev
69ce37c24e, picker-listener.ts still advertises onlyhttp/1.1on the client-facing TLS connection. Ordinary non-bootstrap responses are piped through without collecting their bodies. Your same-process blind-tunnel/intercept A/B therefore supports a client-side connection-slot starvation hypothesis; it is not evidence that another header-limit increase fixes the remaining problem. The exact pool limit and negotiated direct protocol have not been independently established here.The next useful regression is a client with a bounded HTTP/1.1 connection pool holding the long-lived SSE subscriptions, then an ordinary request to the same origin. It must exercise queueing before listener entry; an unrestricted raw-client test opening extra sockets would miss that behavior. Any multiplexing fix needs actual negotiated-protocol and concurrent-stream evidence, plus cancellation/streaming and bootstrap rewriting coverage—not merely advertising
h2to the existing HTTP/1.1 parser.Keeping #6511 open. #6551's header correction remains a separate scoped result, not end-to-end resolution. I have not run the native macOS client or changed a live profile/service.
fix(claude): serve the Desktop picker over HTTP/2 so SSE streams cann…
lidge-jun commentedon Oct 5, 2026
Thanks for the detailed captures; they pinned this down. Your relay logs showed six
messages/streamSSE responses staying open while the timed-out requests never reached the picker. Both point to the same cause: the picker listener only offered HTTP/1.1, so each open stream held one of Chromium's six connections to claude.ai and later requests queued inside Desktop. #6610 (merged todevasaa10846a4c) makes the picker serve HTTP/2, so Desktop multiplexes all claude.ai requests over one connection, as it does when it talks to Anthropic directly. WebSocket connections and clients without HTTP/2 stay on the existing HTTP/1.1 path.If you can retest:
aa10846a4c: the next release, ordevat or after that commit. Restart OpenCodex, turn picker mode on, then fully quit and reopen Claude Desktop.ocx statusshows its path), look for lines starting with[claude-picker]. Soon after Desktop starts you should see[claude-picker] picker session h2, which confirms Desktop negotiated HTTP/2 with the picker. Normal[claude-picker] picker GET other 200lines should follow.signal timed outcomes back over a few minutes.A short redacted reply is enough: OpenCodex and Desktop versions, whether
picker session h2appeared, whether chat loaded and sent, and anypicker request refusedlines (a new guard; there shouldn't be any). Please don't include cookies, headers, bodies or account data. If chat still stalls,ocx claude desktop picker offbrings it back right away, as before.rhomat27 commentedon Oct 5, 2026
Retested on
v2.78.0-preview.20261005, which includesaa10846a4c. I ran the release source on the laptop. The Mini is unchanged.[claude-picker] picker session h2: yes, 2 sessions since the restart.picker request refused: none.signal timed out.This is fixed for us. Thanks for turning it around so fast.
lidge-jun commentedon Oct 5, 2026
Fixed by #6610 (aa10846, in 2.78.0-preview.20261005): the picker now serves HTTP/2, so Claude Desktop multiplexes claude.ai requests over one connection instead of exhausting Chromium's six HTTP/1.1 slots. Thank you for the captures and for confirming on the preview that HTTP/2 negotiated, chat loads and sends, and the 502s and timeouts are gone. If anything regresses,
ocx claude desktop picker offrestores the blind tunnel immediately; please reopen with the redacted log lines.Update from upstream 2.80.0 while preserving fork integrations (#9)