Repository navigation
[macOS Desktop 26.1007, build 20156] model/list expires after 30s in queue; Local Work startup repeatedly times out on plugin/installed #53197
Description
Problem
The macOS desktop app records two recurring control/startup failures:
model/listexpires after approximately 30 seconds in the app-server request queue.- Local Work executor startup repeatedly fails while waiting for
plugin/installed.
Please investigate both the queued-request lifecycle and the plugin-discovery dependency during executor startup. The available evidence does not establish a common root cause.
Environment
- Installed ChatGPT desktop app / Codex compatibility bundle ID:
com.openai.codex. - App version:
26.1007.30912; build:20156, read from the installed application's Info.plist. - Incident logs also report build
20156and commita993bdbe5bb9503cb84de1e67d5635cf186a959f. - macOS:
26.6.2, reported by Python'splatform.mac_ver()during this inspection. - Observation date: October 11, 2026. All timestamps below are UTC.
- Bundled app-server version and subscription tier were not collected.
Verified diagnostic evidence
These are selected, field-preserving excerpts from existing desktop logs. Identifiers, local paths and unrelated fields are omitted.
A. Model discovery expires while queued
2026-10-11T14:56:26.896Z warning [AppServerConnection]
app_server_client_request_queue_rejected
method=model/list source=models priority=background
inFlightRequestCount=8
queuedBackgroundRequestCount=5
queuedCriticalRequestCount=1
queuedInteractiveRequestCount=21
queuedPriorityRequestCount=5
queueWaitMs=30000 reason=queue-expired
2026-10-11T14:56:26.899Z error [electron-message-handler]
Request failed method=model/list durationMs=30002
error={"code":-32001,"message":"App server request expired while queued"}
The rejection record explicitly reports a 30-second queue wait. This should be distinguished from model inference latency or a confirmed remote model-service timeout.
B. Plugin response timeout prevents Local Work executor startup
A separate launch sequence records:
2026-10-11T14:59:11.584Z info [tpp-local-executor]
Starting Local Work executor attempt=1
2026-10-11T14:59:41.335Z warning [electron-message-handler]
mcp_request_timeout method=plugin/installed source=plugins
priority=background queueWaitMs=0 timeoutMs=30000
2026-10-11T14:59:41.586Z warning [tpp-local-executor]
Local Work executor startup failed attempt=1
errorMessage="Timed out waiting for MCP response to plugin/installed"
2026-10-11T14:59:46.080Z warning [AppServerConnection]
app_server_client_request_queue_rejected
method=plugin/installed source=plugins priority=background
inFlightRequestCount=8 queueWaitMs=29999 reason=queue-expired
2026-10-11T15:00:12.602Z warning [tpp-local-executor]
Local Work executor startup failed attempt=2
errorMessage="Timed out waiting for MCP response to plugin/installed"
The same launch log records the same executor failure for attempts 3–6 at 15:00:43.607, 15:01:14.612, 15:01:45.616 and 15:02:16.621 UTC. These are logged startup attempts, not user/model turns.
The immediate demonstrated impact is failed Local Work executor startup. Why plugin discovery does not complete, and whether the model-list queue expiry shares that cause, remain unverified.
Reproduction status
This is a report based on an existing user profile and retained application logs. A deterministic clean-profile reproduction and the initiating user action have not been established. No stress test, configuration change, history deletion or installed-app modification was performed for this report.
Expected behavior / requested investigation
- Model discovery should complete or return an actionable, bounded failure while keeping essential controls responsive.
- Local Work startup should expose a clear plugin-discovery failure and a supported recovery path.
- Diagnostics should distinguish queue expiry from a dispatched request waiting for a response.
- Please investigate in-flight request age/completion and the specific plugin-discovery operation blocking executor startup; the log's count of eight is an observation, not a claim that the scheduler's implementation has been proven.
- Recovery should preserve existing conversations.
Separate authorization error
The same installation also records thread/read / source=thread_hydration returning -32003: thread access denied, including responses with queueWaitMs=0. That is a separate authorization-error class, tracked in #53178. Its co-occurrence does not establish that it causes these queue/startup failures.
Increasing memory or deleting conversation history is not an established remedy for that access denial and cannot be presented as a guaranteed fix. Please preserve authorization boundaries while investigating the read path.
Related reports checked
- [Windows Desktop 26.930] Sent read timeouts retain all 8 host RPC slots; Send/Retry fails until restart #50812: Windows desktop reports involving eight occupied host RPC slots and read timeouts.
- Windows + WSL: dot stays offline; plugin/installed times out during local executor startup #51905: Windows/WSL Local Work startup waiting for
plugin/installed. - [macOS Desktop] Persistent access-denied background hydration retries alongside high renderer CPU; restart does not resolve #53178: macOS repeated access-denied background hydration.
These are related signatures, not confirmed shared root causes; please consolidate if appropriate.
Submitted by Codex, the AI assistant, at the user's request. This public report contains only version information and selected sanitized diagnostic fields.
Activity
github-actions commentedon Oct 11, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
0xdevalias commentedon Oct 17, 2026
Independent current-build macOS confirmation, with a distinction between queued expiry and dispatched-request abandonment.
In the initial captured interval: 631 queue rejections, including 29 critical account/auth rejections. At 11:20–11:21 AEDT there were 243 distinct observed
plugin/installedrequest IDs (121 responses + 122 failures), 49account/readand 51getAuthStatusIDs. Failures in the first burst involved ten renderer windows. These are requests, not an asserted fixed demand per window. At the original stuck prompt's 12:48 enqueue there was no queue rejection or transport closure, so these bursts do not establish that prompt's initiating cause.Actual host scheduler
vRinsrc-DCNiwad_.js: total active 8; noncritical active <=5; background active <=3; queued caps background 128, critical 16, interactive 64. Eight is the TOTAL active limit, not eight slots reserved for critical work. Critical requests can use remaining total capacity. There are additional metadata/thread/catalog lane limits.plugin/installedis absent fromhR's coalescing allowlist (plugin/listis present).Actual-code tests show 132 identical plugin-inventory requests produce 3 active + 128 queued + 1 rejection. Background fullness alone does not reject critical work: account/auth fan-out can additionally fill the remaining active slots and all 16 critical queued slots, after which critical requests are rejected. The real rejection snapshots reach these limits.
Demand: shared asset
XZrinventory query has 30,000-ms timeout, retry=false, staleTime=0; outerW2UI/executor queries include consumer/root/host keys, with local staleTime one minute (hosted five minutes). StaleTime is eligibility for another fetch, not proof of a once-per-minute polling timer. Different renderers and keys multiply demand. Account/auth state uses pairedgetAccount/getAuthMethodcalls and readiness/auth-status/RPC-recovery listeners;FGr/IGrdeduplicate only in their renderer/client context. Turn preparation and other consumers also call these methods.refreshToken:falseis used on the underlying reads, so this is not evidence that every read refreshes an expired token.Dispatched timeout retention: extracting and invoking the actual
abandonDestinationmethod with a dispatched ordinary window request leavesplugin/installed,account/read, andgetAuthStatusinpendingRequests; it does not call scheduler completion. Positive controls: dispatchedplugin/listis detached andthread/resumecompletes. A renderer timeout is therefore not necessarily host-slot cancellation. A late response or connection failAll/disposal is needed for ordinary dispatched reads to release their slot. This agrees with #50812; closing a window is not a guaranteed cancellation of dispatched host work.Of 151 renderer timeouts, 148 have no matched
response_routedin the reviewed logs; that absence alone does not distinguish queued expiry from dispatched retention. Two matched plugin calls spent approximately 29.7–30.0 seconds queued and only 483/556 ms executing, exceeding their renderer deadline. No current live pending inventory was available to identify every retained request or prove that all eight remain poisoned now.Fix/diagnostics: host-level single-flight/cache for inventory and account/auth reads; admission/backpressure; consistent end-to-end deadlines; explicit cancellation or safe detached-read accounting; age/method/origin/connection-generation inventory for in-flight and queued requests; oldest age, depth/high-water, coalesced/rejected/expired counts and actionable degraded-state UI. These are Desktop main-process queues, separate from Rust app-server
queue_1.sqlite. Hardcoded JS limits are not a supported user configuration. Enlarging caps alone can prolong stalls or increase backend load. No defensible maximum number of open windows follows from eight RPC slots; one busy window can fill a lane and retained renderers also contribute demand.Related: #50812 and #43525 (shared queue/control latency); #53677 (avoidable denied catalog reads, usually fast rather than continuously occupying slots here).
Verified 2026-10-17: Codex Desktop macOS arm64 26.1007.71326, build 20398, macOS 26.6 (25G72). Installed archive
Contents/Resources/app.asarSHA-2562f231a64e6dd30111bcd09e03c149cd06c8679b25914842772423251c4608ded.Build-specific source provenance:
webview/assets/app-shared-8340f229c2b3.js: SHA-2563b4cabc382e71f0da7398393a619d7c62d13dfae598a5fe2e9880ab8d31d1bf5..vite/build/main-CwxgTOle.js: SHA-25687d52265eb13608a380fabcac47ecf13a39b981115c0960c18af563ea6d32d51..vite/build/src-DCNiwad_.js: SHA-25686fb2066bbc2f7428864b76b46ef570b09eb1e92e81e9d6f089f5b048a66df25.Source observations below are version-specific. Offline probes evaluate extracted shipped methods in a Node VM with synthetic dependencies; they do not establish an unlogged live race. No app patch/restart, queue edit, direct send, or SQLite mutation was performed. Private text, annotations, account identifiers, task IDs, and raw logs are excluded.
Build-specific offline runner: Python 3 + Node.js v26.8.2 used
Save and run this Python script against the stated build. It reads the archive and uses a temporary directory only. A different build may change extraction boundaries.
The abandonment probe can be reproduced by extracting
abandonDestination(e,t){...}through immediately beforecomplete(e){from the main asset, evaluating it as an object method, and calling with a synthetic window destination,startedAtMs:1, empty coalesced map,retainUnknownOutcome:()=>false, andrequestScheduler.dropUnsentRequest:()=>false. Assert pending remains for the three ordinary reads and is removed for the two controls. This isolates the shipped abandonment branch; it is not a simulated backend hang.sivdead commentedon Oct 17, 2026
Additional macOS reproduction on a newer build: Send becomes unresponsive while the desktop app-server scheduler remains at 7–8 in-flight requests. A manual full app restart restores responsiveness.
Environment
Observed behavior and timeline
Selected redacted log excerpts
The following preserve the diagnostic fields. Conversation/request IDs, local paths, process IDs, window IDs, business domains and unrelated fields are omitted. No raw conversation content is included.
Read-only comparison after recovery
We launched separate temporary app-server processes for the bundled backend and the separately installed CLI. For each, we initialized the protocol and sent three concurrent thread/turns/list requests for the same persisted conversation with limit=1 and itemsView=full. Each response was 368,183 bytes and valid JSON. All three completed in approximately 0.16 seconds total with the bundled backend and 0.23 seconds with the separate CLI. The temporary processes were then stopped. No turn/start, conversation deletion or history modification was performed.
This does not reproduce or disprove the intermittent failure, nor prove that changing backend versions fixes it. It does show that the persisted history was readable through an independent protocol connection after recovery.
Potential recovery gap to investigate
Read-only inspection of this installed desktop build suggests that markSentToTransport clears the scheduler entry's expiration timer. complete removes an entry after a response, while expireRequest does not expire an already-sent entry. The stdout JSONL parse-error handler logs the failure but does not itself identify/complete the affected RPC.
Please verify whether a lost/unparseable response can therefore leave a dispatched entry occupying a slot indefinitely, and whether caller-side timeouts release or cancel that same scheduler entry. We could not correlate an individual malformed message with a specific permanently occupied request, so this remains a hypothesis rather than a proven root cause.
Requested behavior
Related signature: #38385 describes a similar scheduler recovery gap on Windows. This comment adds macOS build 20770 evidence; a shared root cause is not yet established. No deterministic clean-profile reproducer is available.
Submitted by Codex at the user's explicit request. Only selected sanitized technical evidence is included; no full logs, session IDs, screenshots, credentials or conversation contents are uploaded.