Skip to content

Remote (Android) open times out on large threads while the host replies instantly #55005

Open

Description

@kulear829

Environment

  • Codex desktop 26.1013.4894.0 (MSIX) on Windows 11 10.0.26300
  • Remote client: Android app (app_server.client_name="codex_chatgpt_android_remote")

Symptom

Opening a long-running thread from the phone shows a load timeout and never renders. Short threads open normally, and the same threads load fine on the desktop client. Reported to feel worse after updating to 26.1013.4894.0, but I have no measurements from earlier builds, so I am not claiming a regression.

Measurements (same machine, same Wi-Fi, 7 threads, opened one by one from the Android client)

Thread (first 8 of id) Items thread_items size Turns Remote open
01a13b71 981 137.8 MB 47 timeout
01a0f4c0 3,754 157.4 MB 181 timeout
01a0f10a 3,786 5.9 MB 191 timeout
01a0f93c 81 0.1 MB 4 ok
01a0f978 242 0.8 MB 11 ok
01a0adb8 106 0.2 MB 11 ok
01a0a36a 138 0.3 MB 35 ok

Every failure has ≥981 items; every success has ≤242 items.

Size is not the explanation: 01a0f10a is only 5.9 MB and still times out. It carries 3,786 items but almost no image data. So this is not bandwidth, not image payload, and not the network.

Host-side evidence (sanitized desktop logs, same second as each failed open)

  • The host answers immediately and without error: thread_lifecycle: composing running thread resume response thread_id=01a13b71-… lands in the same second as the request, with errorCode=null.
  • No error code ever reaches the client — nothing like -32003. The request simply never completes on the remote side.
  • The only client-side line is Conversation state not found conversationId=…, which also appears for threads that open fine; it is noise, not the cause.
  • For the same threads, desktop-side thread/read, thread/turns and thread/items/* return in ~200 ms.

In short: the host prepares the whole history and replies at once; the remote client cannot get it across its timeout.

How to reproduce

  1. Create a thread with ~1,000+ items (a long asset/image session reaches this in a few hundred turns).
  2. Connect the Android remote client to the desktop host.
  3. Open that thread remotely → timeout. Open a small thread → fine.
  4. Open the same thread on the desktop → fine.

Expected

Remote open should behave like desktop: first screen renders quickly, older history streams in lazily. A thread should not have to transfer its entire item history before it becomes usable.

Suggestion

  • Paginate hydration for remote clients: return the last N turns/items plus a cursor, and pull earlier ranges on scroll (thread/items already accepts ranges).
  • Cap what a single thread/resume response may contain, independently of thread size.
  • Secondary, size only: imageGeneration.result keeps the full PNG as base64 inside item_json. On this machine that is ~2 GB across 751 items, up to 971 MB in one thread. Keeping only savedPath would shrink payloads by 10–50× — worth doing, but it will not fix 01a0f10a, which is 5.9 MB.

All numbers above are row counts and byte sizes from the local thread database. No conversation content, file paths, account identifiers, or credentials are included.

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    app-serverIssues involving app server protocol or interfaces
    connectivityIssues involving networking or endpoint connectivity problems (disconnections)
    on Oct 18, 2026
  2. github-actions commented on Oct 18, 2026

    @github-actions
    Contributor
  3. kulear829 commented on Oct 18, 2026

    @kulear829
    Author

    Related: #54751 — same remote pagination timeout, and one data point that changes the fix

    #54751 (iPhone, same stack) already traces this to thread/items/list returning oversized pages because thread_resume_redaction.rs strips imageGeneration on thread/resume but not in the paginated reads. That explains the image-heavy threads.

    My case adds a counterexample that a byte budget alone would not cover.

    01a0f10a times out with 5.9 MB total and 3,786 items — and no inline image payload at all. It has 345 imageGeneration records, but every one of them has an empty result, and 93% of their combined 1.5 MB is revisedPrompt text. Average item size is ~1.6 KB, so a 25-item page is roughly 40 KB.

    That thread should be cheap to fetch by any byte measure. It is simply fetched ~152 times in sequence, and the client gives up before first paint. Add a byte budget without a page budget and this thread still needs 152 round trips, so it still times out — it is the clean control case.

    At the default 25 items per page, in my measurements the boundary looks like page count rather than bytes:

    • ~152 pages (3,786 items, 5.9 MB, no image bytes) → timeout
    • ~151 pages (3,754 items, 157 MB) → timeout
    • ~40 pages (981 items, 137 MB) → timeout
    • ~10 pages (242 items, 0.8 MB) → ok
    • ~4 pages (81 items, 0.1 MB) → ok

    So the fix likely needs two parts:

    1. a byte budget per page for image-heavy threads — the case already described in #54751, and
    2. avoiding N sequential round trips before first paint for item-heavy threads — e.g. return the tail of the thread in the resume/first response and page backwards, or raise the page size when items are small.

    Host side is not the bottleneck here: thread/resume composes in <100 ms with errorCode=null, and desktop-side thread/read / thread/items take ~200 ms on the same threads. Everything up to the wire is fast; the remote client is the one that gives up.

  4. kulear829 commented on Oct 18, 2026

    @kulear829
    Author

    Correction to my "page count rather than bytes" line — both dimensions can blow the budget

    In my previous comment I wrote that the boundary "looks like page count rather than bytes". That is too strong, and #54751's own measurements show why: their control thread needs 33 pages but only 1.3 MB and opens fine, while their affected threads time out at 19 pages / 15.3 MB (largest page 10 MB) and 22 pages / 7.7 MB. Fewer pages, far more bytes, still fails.

    Putting both datasets on the same two axes:

    Thread Pages Bytes Result
    #54751 control 33 1.3 MB ok
    #54751 affected B 22 7.7 MB (max page 4.5 MB) timeout
    #54751 affected A 19 15.3 MB (max page 10 MB) timeout
    my 01a0f10a 152 5.9 MB, no image bytes timeout
    my 394-item thread 16 0.82 MB ok
    my 01a13b71 40 137 MB timeout

    Neither axis separates the groups on its own. 19 pages is low by count but fails on bytes; 152 pages is trivial by bytes but fails on count. What separates them is the combination — consistent with a single deadline that either dimension can exhaust, not with a page cap or a size cap alone.

    So the two-part fix still stands, and it needs both halves:

    1. a per-page byte cap — without it, a 10 MB page still stalls the client at 19 pages;
    2. bounded sequential round trips before first paint — without it, 01a0f10a still walks 152 pages of 40 KB each and still times out.

    Also worth noting for whoever reproduces this: 01a0f10a timed out twice tonight about 20 minutes apart. Both times the host logged composing running thread resume response a couple of seconds after the request with no error code, so this is deterministic rather than network flakiness.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appapp-serverIssues involving app server protocol or interfacesbugSomething isn't workingconnectivityIssues involving networking or endpoint connectivity problems (disconnections)remote

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions