Repository navigation
Android Remote thread/list omits agent-created threads and returns duplicate IDs; early user event restores visibility #43017
Description
Summary
Android Remote omits valid desktop threads before rendering: a captured thread/list response from the Mac was already incomplete and contained duplicate IDs. A controlled A/B/A test shows that inserting an original task assignment as an early event_msg.user_message restores one affected thread to the filesystem listing. This also restored access on the actual phone.
Related: #22777 (early preview scan), #25589 (iOS empty listing), #24749 (Android visibility). This report adds recent-version reproduction, duplicate counts, and a verified mobile workaround. It does not establish that all of these reports share one cause.
Environment
- macOS host, Desktop 26.901.31953 (7868)
- Bundled codex-cli/app-server 0.153.1
- Android client version documented during this investigation: 1.2026.237 (16)
- Local desktop threads, ChatGPT/OpenAI provider
- Investigation: September 4–5, 2026
- Earlier symptoms reproduced on two Android phones; reconnecting/re-pairing did not resolve them
Host-side evidence
The captured mobile request used thread/list, limit 50, updated_at descending, cursor null. No useStateDbOnly appeared in the retained parameters. Capture retained selected parameters and response metadata, not complete raw traffic.
The host replied with:
- 25 records but only 14 unique thread IDs
nextCursor: null- Only 2 threads from a local project with 20 non-archived root vscode threads in SQLite
Thus this particular omission cannot be explained solely by Android hiding correctly received records. Duplicate multiplicities matched the number of JSONL history files for the affected IDs; those files may be legitimate history continuations.
On an isolated copy, using the installed binary:
useStateDbOnly: falsereproduced 25 records / 14 unique / 2 project threads.useStateDbOnly: truereturned 17 unique / 4 project threads.- Filling one empty preview in the copied state DB restored that thread only to the SQLite listing. The filesystem listing remained unchanged.
Controlled reproduction / workaround
For one completed agent-created thread, its original assignment was recovered from the parent create_thread call and matched to the returned child ID.
In an isolated copy, insert one event_msg.user_message containing that assignment immediately after session_meta, without altering the rest of the JSONL:
| Phase | Filesystem records / unique | Project threads | Target present |
|---|---|---|---|
| Original | 25 / 14 | 2 | No |
| Early user event inserted | 26 / 15 | 3 | Yes |
| Insertion removed | 25 / 14 | 2 | No |
In separate control/patched copies, thread/read, full thread/turns/list, and thread/resume succeeded. The returned 289 history items matched by canonical JSON hash before/after resume and between copies. No new model turn was run.
After backing up and applying this one-thread workaround on the real host, the user confirmed both visibility and opening the original final answer on Android. This is a workaround, not evidence that rewriting histories is a supported repair.
Creating a thread through an agent is a correlation, not a sufficient condition
Of 34 agent-created/forked threads compared with mobile screenshots:
- 3 were visible without repair and had user messages.
- 1 became visible through the workaround above.
- 30 were absent. Of those, 27 had no projected userMessage, but 3 had a real user follow-up in a second turn and still did not appear.
Therefore “send one message” is not yet a proven general workaround.
Separate project-list observation
Android project groups matched working-directory names rather than Desktop display names/icons. Archiving the last visible user thread in each of two otherwise unsaved desktop folders immediately removed both mobile groups. Renaming another working directory and updating its references eventually changed the mobile group name, while its Desktop display name stayed unchanged.
This supports grouping by thread working directories, but we have not inspected Android implementation. It may need a separate issue.
Expected behavior
Valid threads should not be silently excluded solely because a filesystem preview scan misses a user event when their history/metadata exists elsewhere. Listings should deduplicate logical thread IDs across history segments and provide complete pagination.
Please investigate the default filesystem scan/repair listing versus the state-DB-only path, particularly agent-created and paginated histories. No private paths, thread IDs, raw logs, or conversation contents are attached.
Activity
84dnnvbdvp-debug commentedon Sep 5, 2026
Current main still has a source shape that matches both symptoms in this report.
In
codex-rs/rollout/src/list.rs, the filesystem listing paths enumerate rollout files and build oneThreadItemper file.build_thread_item()only returns an item whenread_head_summary()finds session metadata plus a discoverable preview; that scan looks at the first 10 records and may extend up to 200 more while searching for a preview/user message. The traversal then pushes matching items directly into the page. I do not see a logicalthread_iddedupe step before page sizing/cursor construction.That suggests one focused regression can cover the two observations together:
SessionMetaentries share the same logical thread id. Put the preview-bearingevent_msg.user_messageonly in a later segment (or otherwise outside the first segment's summary window).thread/listwith the normal non-state-db-only path.Today, file-level enumeration can explain duplicate IDs when one logical thread has multiple history files, while the preview gate can explain omission when the segment being summarized lacks an early preview-bearing event. The reporter's A/B insertion immediately after
session_metais especially consistent with that gate.A fix probably needs to define the canonical listing unit as a logical thread id rather than a rollout file (or explicitly coalesce file candidates by
ThreadItem.thread_id) before applying page limits/cursors. If preview recovery is intentionally bounded, the regression should also define which segment/history source is authoritative for discovery so a valid later user follow-up cannot remain invisible merely because the first scanned segment has no preview.This is source-level narrowing only; it does not establish that Android has no additional client-side issue.
jfmoe commentedon Sep 6, 2026
Additional reproduction on September 6, 2026. This adds evidence about how the initial task reaches an affected thread: it is stored as a delegation tool output, while the normal user-message and preview fields remain empty.
Environment
Reproduction
codex_app.create_threadwith the prompt, a title, and a local project target. The tool returns a new thread ID andhostId: "local".codex_app.read_threadsucceeds andnavigate_to_codex_pagereports success. However, repeatedcodex_app.list_threadscalls, including a limit of 50, omit the new thread from both normal and pinned results. Existing conversations from the same project are returned.Initial prompt is present, but not as a user message
The new thread's first history item is:
Its output contains a
codex_delegationblock. The block'ssource_thread_ididentifies the originating conversation, and itsinputcontains the complete requested implementation prompt. Subsequent assistant messages respond to that prompt. Thus the task received its instructions and started execution; the missing list entry is not evidence of a missing assignment.Read-only inspection of the thread metadata found:
The projected history contained one
functionCallOutputand twoagentMessageitems, with nouserMessageitem. A normal visible conversation in the same project had nonemptyfirst_user_messageandpreviewfields.Impact and limits
The new task was still
activewithwaitingOnApproval, making mobile discovery useful for handling the pending action. A follow-up throughcodex_app.send_message_to_threadreturned successfully, but subsequent reads still showed no user-message item, an empty preview, and no list entry. This does not establish that a consumed follow-up would fail to help; the task remained blocked on approval.I did not capture this phone's raw
thread/listtraffic, reproduce duplicate IDs, or modify the live database or history files. The empty preview is a correlation in this reproduction, not an independently proven cause of the mobile filtering.Expected behavior: a user-requested top-level task created through
create_threadshould remain discoverable in desktop and Remote lists, including while waiting for approval, even when its initial assignment is delivered as a delegation tool output. It should not require an ordinary user-message event to become visible.Joris-MEJOR commentedon Sep 7, 2026
Additional Linux desktop and CLI reproduction on September 7, 2026, including isolated repair tests. This extends the affected surfaces beyond mobile and matches the difference between the database-only and filesystem scan paths described above. Related: #32614.
Environment
This rules out a sudo/user-storage difference or different Linux backend binaries for this reproduction. It does not establish that the two desktop clients have identical caches or listing behavior.
Observed behavior
codex_app.create_threadreceives its assignment, works, and remains visible in the originating Mac client's UI.thread/listprobe without a working-directory filter returns four threads withuseStateDbOnly: true, but only two withuseStateDbOnly: false. The two match the entries shown by the CLI. Two selected missing tasks are omitted in both modes.thread/readby ID withincludeTurns: truereturns the missing tasks' stored turns successfully.The missing tasks have the following metadata:
The initial assignment is preserved as a
function_call_outputforcodex_app.create_thread, inside acodex_delegationenvelope. Subsequent agent activity responds to that assignment. The projected turn history contains no ordinaryuserMessagefor the initial assignment.Inspection of the installed desktop bundle also shows that its local recent-thread request uses the scan-and-repair path, while the remote recent-thread request uses database-only listing. That helps explain differences between surfaces, but we did not capture raw traffic from the active Linux UI.
Isolated repair tests
Tests used private SQLite backups and copied JSONL histories with a temporary
CODEX_HOME. Live chat histories, metadata, app configuration, and packages were not edited.thread/name/set, using the existing display nametitleandpreviewfieldstranscript_v2only in the diagnostic backendThe ordinary-message test used an isolated working directory, read-only sandbox, and an unauthenticated model provider pointing to an inactive loopback port with retries disabled. The message and an additional turn were persisted, but no real model response or repository work completed. Queries after restart included all providers. This is evidence for that isolated case, not a claim that sending a message is a general repair.
Direct full-history reads continued to succeed, and a copied task resumed successfully. We did not test or apply an early-event insertion workaround, modify live histories, or test a source-code patch. We also did not independently reproduce duplicate IDs in backend responses.
Expected behavior
A deliberately created, named, non-archived top-level task should be discoverable when its assignment and execution history exist, even if its assignment arrived through delegation rather than an ordinary user message. Database-only listing and filesystem scanning should agree on its eligibility. Any repair should preserve message provenance and cover existing affected tasks as well as future creation.
No private task IDs, names, assignment contents, hostnames, usernames, project paths, screenshots, credentials, or raw chat logs are included in this report.
alanagoyal commentedon Sep 7, 2026
Additional reproduction on September 7, 2026: scheduled automation tasks on a Mac mini host were visible in the host desktop sidebar but missing from both iPhone Remote and a second Mac's remote sidebar. Existing remote tasks remained operable.
Environment:
One completed scheduled run was confirmed unarchived (
archived=0), withsource=vscode,thread_source=automation,history_mode=paginated, a nonemptyname, and emptypreviewandtitlefields.has_user_event=0. Direct thread read by ID returned its intact completed history.Using the installed app-server,
thread/listwith the exact task working directory,archived=false, andlimit=100returned no entry for this task with eitheruseStateDbOnly=trueorfalse.Controlled tests against temporary SQLite copies (including state and paginated history databases), using
useStateDbOnly=true:After backing up the original metadata, filling only the preview on the live host restored the task to the live desktop list_threads tool. The same repair restored three other unarchived, named automation tasks with paginated history. No transcript content, archive flags, or timestamps were changed. Remote-device UI recovery has not yet been confirmed by the user, so this establishes host-side listing recovery rather than verified end-to-end recovery.
This looks related to the empty-preview filtering described above, but this case involves scheduled automation runs and does not establish a shared cause for duplicate IDs or filesystem-listing behavior. We have not established why these previews were initially empty. A real completed automation task with a name and intact history should remain discoverable even when its preview is empty.
No private paths, task identifiers, raw logs, or conversation content are included. The metadata edit was a tested workaround, not a supported or permanent product fix.
Beword2024 commentedon Sep 8, 2026
Additional Windows / Android reproduction on September 8, 2026, with a controlled record-210/211 boundary test using the installed app-server. This corroborates the listing omission; it does not independently reproduce the duplicate-ID symptom.
Environment
Request-level reproduction
The Android recent-list request shape identified in the installed client is:
{ "method": "thread/list", "params": { "cursor": null, "limit": 50, "modelProviders": null, "sortKey": "updated_at", "sortDirection": "desc" } }Using the same installed app-server executable against an isolated copy containing two affected threads and two visible controls:
These are backend probe results, not a claim that the Android app was patched or that the end-to-end problem is fixed.
Controlled scan-boundary test
The affected tasks were created through create_thread. Their initial assignment is stored as FunctionCallOutput containing a codex_delegation envelope, not as an ordinary user-message event. Six observed missing tasks have their first actual ItemCompleted(UserMessage) at records 216, 336, 432, 584, 902, and 935. Three visible controls have it at 10, 140, and 168.
On one isolated fixture, using the same executable and moving/copying the fixture's existing user event to control its first position:
Only the isolated fixture history was changed for this experiment. Original conversation histories were not rewritten.
This matches the preview eligibility gate in codex-rs/rollout/src/list.rs: HEAD_RECORD_LIMIT=10 plus USER_EVENT_SCAN_LIMIT=200, with build_thread_item requiring a discoverable preview. The initial delegation tool output does not supply that user-event preview. The state-DB-only alternative is in codex-rs/thread-store/src/local/list_threads.rs. These are source pointers, not an assertion that current main exactly matches the deployed binary.
Requested correction
Please ensure valid named tasks remain discoverable when their first actual user event is beyond the bounded rollout scan, or when their initial assignment arrives through delegation. The Android recent-list request using useStateDbOnly=true is a verified candidate for recovering these four fixture entries; it still needs product-level validation and does not establish a general fix for empty-preview cases discussed above.
Please add regressions for records 210 versus 211 and for delegated tasks without a direct user message yet. A permanent fix should preserve message provenance rather than require users to insert fabricated user events into their histories.
Separate unresolved symptom
Pinning exposed one missing task on Android, but opening it remained loading; unpinning made it disappear from the normal list again. Its history is present and readable on the host. The specific cause of Android content loading has not been established, and the listing experiment must not be interpreted as fixing it.
No private task names/IDs, account identifiers, hostnames, filesystem paths, screenshots, raw logs, credentials, or conversation text are included.
lisakhan commentedon Sep 12, 2026
Additional scheduled-automation reproduction on September 12, 2026. This isolates title/preview persistence from the manual archive action and adds a standalone scheduled-task case to the existing empty-preview listing reports.
Environment
Reproduction
The specific reproduction above was confirmed after manually archiving the completed scheduled-task chat. The same empty title and preview metadata shape was observed in other completed runs; I am not asserting that every other run was manually archived.
A still-active run from the same task was also omitted from the normal task listing before it was archived.
Metadata evidence
The affected archived records have this shape:
The still-active scheduled run has the same empty title and preview while archived: 0. A normal user-created archived chat has has_user_event: 1 and populated title and preview fields.
This shows that the manual archive action is not the point where the fields are first emptied: scheduled automation threads are created without title/preview metadata, and the archive/listing path then makes the manually archived run undiscoverable.
Controlled recovery and limits
Expected behavior
After a user manually archives a completed scheduled-task chat, it should remain discoverable in Archived Chats with a stable task title and preview. Standalone scheduled-task runs should also remain discoverable in Scheduled with a stable result preview. Listing should not require an ordinary user-message event for a valid named automation run.
This is a scheduled-automation-specific reproduction related to the existing empty-preview/listing reports in this issue. The transcripts are intact; the observed failure is metadata/discovery, not deletion.
No private task IDs, filesystem paths, prompts, logs, account details, or workspace contents are included.
kernelpanic888 commentedon Sep 14, 2026
New reproducible evidence on 0.154.0
Related issue: #43017.
This adds a small synthetic app-server reproduction to the existing preview/deduplication analyses. It does not test Android itself or claim one cause explains every missing task.
Environment: macOS 26.5.1 arm64, official codex-cli/app-server 0.154.0, Node 24.19.0. Release commit:
36eab01061df3cde5f95ec20a526777b430091ba. Tests use an isolated subprocess home, no credentials and no model turns.The harness writes synthetic rollout files following the repository's rollout test fixtures, initializes stdio JSON-RPC, then calls
thread/listwithuseStateDbOnly:false,modelProviders:[], a case-specific cwd and descending sorting. Bothcreated_atandupdated_atshow the following results:Thus the duplicate-ID observation can be reproduced without personal histories. Equal-timestamp pagination is a separate omission mechanism even when all four previews are immediately discoverable. The preview boundary merely corroborates the earlier comment; it is not a new discovery.
For equal timestamps, the first nextCursor is
2026-01-01T00:01:00Z; using it produces an empty second page. Supplying the supportedtimestamp|idcursor syntax manually, with the last ID from page one, also produces an empty result on the filesystem path.Narrow code explanation / proposed contract
The release list implementation skips every candidate whose timestamp is equal to or newer than the anchor, ignoring
_id. Cursor construction emits timestamp-only cursors for created/updated ordering. These code paths are identical in checked maine9633d7a0226eac91c7a791dc4f92cf8f25df2ae.The fix contract should independently cover:
Reproducer:
app-server-harness.mjspluslist-repro.mjs; runnode list-repro.mjsbeside the official binary namedcodex-aarch64-apple-darwin. The script stores generated fixtures, requests, responses and a result summary. Its assertions confirm the observed bug signatures, not a corrected implementation. No Rust patch, source build or mobile UI verification was performed.Alexey Salkutsan selected the investigation. Lina, an AI assistant, authored the synthetic harness, ran the checks and prepared this analysis. Earlier issue reports and analyses retain their original authorship.
Reproducer package
Download the synthetic reproductions and reader audit. The same small packet supports this issue and the related investigations in #43017, #43129 and #38611. It contains no personal histories, databases, raw RPC logs or bundled executable. Original third-party reader code is fetched by the setup with a pinned hash and attribution.
Verified after extraction into a clean directory on macOS arm64 with Node 24.19.0. Read
README_EN.md, then runnode bootstrap.mjsandnode verify.mjs --portable; setup downloads pinned public dependencies and the official binary. The clean verification used the already downloaded, hash-verified official binary archive with--archive; it fetched the reader and npm dependencies afresh. Linux, Windows and the uncached binary-download branch were not tested. The ZIP contains the earlier preparation drafts, whose NOT SENT headings describe the pre-publication snapshot.ZIP SHA-256:
2d60212455f72dddfd52410b3c8cc55e826ba2900e0813e26c070d65566972fb.shinvou commentedon Sep 14, 2026
Still reproducible after desktop update — September 14, 2026
Adding a new-version retest of the missing-thread / empty-preview symptom in this issue. This does not reproduce the duplicate-ID symptom or establish Android-specific behavior.
Environment
CODEX_HOME; this is not a comparison between two accounts' stores.Request-level reproduction
For each executable, start a fresh
app-serversubprocess over stdio, initialize withcapabilities: { "experimentalApi": true }, sendinitialized, then call:{ "id": 2, "method": "thread/list", "params": { "limit": 100, "useStateDbOnly": true, "sourceKinds": ["vscode", "cli", "appServer", "exec", "unknown"], "modelProviders": [], "sortKey": "updated_at", "archived": false } }Read-only metadata inspection finds 10 non-archived top-level threads with source=vscode. Archived and subagent records are excluded from that expected count.
The smaller request
{"limit":100,"useStateDbOnly":true}produces the same result with both executables. There is no cwd or search filter.The same two tasks are omitted in every test. Immediately afterward,
thread/readwith each omitted ID andincludeTurns:falsesucceeds on both executables, returning the correct ID and a nonempty name. That is a successful summary read, not a claim that full-history API reads were retested.The omitted tasks are not unused empty sessions
Both have this metadata shape:
Read-only inspection of the persisted history projection finds:
This corroborates the existing empty-preview reports: named tasks with saved execution history remain undiscoverable even with database-only listing after the update. It is not an independent A/B proof of the precise predicate in the new binary.
Expected: existing, deliberately created top-level tasks with names and saved work should remain discoverable regardless of whether their initial assignment produced an ordinary user-message preview.
Scope: this retests existing affected tasks, not creation of fresh tasks on the new release. No model turns were started, no tasks were resumed, and no history/preview repair was applied. The filesystem scan path and the current iOS UI were not retested in this update check.
No private task names, IDs, paths, prompts, screenshots, account details, credentials, or raw histories are included.
malyarq commentedon Oct 6, 2026
Additional Windows reproduction and a successful user-message workaround, October 6, 2026.
The same Windows host's chats were accessible through remote control from another PC or Mac, but many were missing from the phone's Remote list and could not be opened using direct links.
Environment:
Before the workaround, read-only inspection found 24 active source=vscode threads. codex_app.list_threads with limit=50 returned 10 of those from the local host. All 14 omitted threads had thread_source=agent_created_thread, zero projected userMessage items, empty first_user_message and empty preview. They had saved agentMessage items and were not archived. Direct codex_app.read_thread succeeded for one omitted thread and returned its completed work.
The user then manually sent one ordinary message in 13 of the 14 affected chats using the desktop app: "Check chat visibility. Do nothing; reply only 'ok'" (in Russian). One affected chat received no message and served as a control.
All 13 changed chats also acquired nonempty first_user_message and preview fields. The control retained empty fields. The user subsequently confirmed that the changed chats were visible and could be opened on the phone.
This reproduces the missing-thread symptom on Windows and demonstrates that an ordinary desktop user message restored access for these 13 existing chats. It does not prove a universal workaround or the precise filtering predicate. No raw mobile RPC traffic was captured, and duplicate IDs were not tested. has_user_event was 0 in both visible and omitted records, so that field alone did not distinguish them here.
Expected behavior: deliberately created top-level tasks with saved work should be discoverable and openable through mobile Remote before the user adds a separate ordinary message. The difference between phone and desktop remote clients may be useful when comparing their listing paths.
The inspection did not modify databases or history files. No private chat titles, thread IDs, paths, prompts beyond the neutral test message, or account data are included.
cooloh123 commentedon Oct 9, 2026
Windows Codex Desktop / Android Remote: version-matched confirmation (October 10, 2026)
Adding a Windows reproduction with a newer bundled app-server version and a same-host manual-control comparison. No private prompts, thread IDs, account identifiers, local paths, or logs are included.
Environment: Windows Codex Desktop, bundled codex-cli/app-server reporting
0.162.0-alpha.2; ChatGPT Android Remote paired to the same host/account (Android build not captured).Observed: Tasks created through the Desktop's supported
codex_app.create_threadtool complete, remain accessible in Windows Codex, and are readable by their canonical thread IDs, but the user cannot discover those tasks in Android Remote. A thread manually created in Windows Codex during the same period does appear on Android.Read-only comparison of two affected tool-created root threads and one visible manual root, using local metadata fields only:
source,originatorvscode,Codex Desktophistory_mode,archivedpaginated,0thread_sourceagent_created_threaduserpreviewlength0first_user_messagelength0name00The two affected tasks each have a durable local job → native-thread-ID mapping, completed results, and intact rollout/index entries. Their native direct-read identity succeeds; native read visibility is not evidence of mobile list visibility. There is no account mismatch, archive, or missing history file in these controls. The
has_user_eventfield was0for the visible manual control too, so it cannot by itself explain the difference.At public tag
rust-v0.162.0-alpha.2,state/src/extract.rsdoes not derivepreviewfrom an initial delegatedFunctionCallOutput;state/src/runtime/threads.rsfilters ordinary active listing withthreads.preview <> ''. The publicmainsource now contains delegated-output preview extraction, but release inclusion, existing-record backfill, and the actual Androidthread/listrequest path were not verified. This supports the empty-preview hypothesis without proving a single Android-specific root cause.A separate offline reproduction using mock native calls, an in-memory SQLite database, and the bundled public schema passed 11/11 focused tests: exact read while ordinary list omits the same ID, one request key/no duplicate task, UNKNOWN no retry, sender/provenance constraints, preview filter and parent-edge behavior. These are not a real mobile or app-server acceptance test.
Requested upstream behavior: Preserve legitimate delegated provenance while allowing completed, named user-facing agent-created root tasks to appear once in Desktop/Remote listings, ideally including already-existing paginated tasks. Please test both state-DB and filesystem/scan listing paths, preview/backfill, deduplication, and retention of the same canonical thread ID and completed result across Windows/Android. Avoid requiring fabricated user events,
thread_source='user'rewrites, new shadow threads, or resending completed tasks.No installed code, real session history, SQLite metadata, active runtime, user task, or Provider SEND was changed during this investigation.