Android Remote replays one create_thread operation into seven thread/start requests

Open 💬 4 comments Opened Jul 31, 2026 by yuya2718
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Bug report: Android Remote creates multiple tasks from one create_thread call

Summary

While controlling Codex on a Windows host from the ChatGPT Android Remote surface during a Realtime Voice session, a request to create one new projectless task produced seven separate top-level tasks.

This was not only a duplicated UI entry. The host persisted seven distinct threads with different thread IDs, turn IDs, rollout files, and projectless working directories.

Environment

  • Host OS: Windows
  • Codex desktop build: 26.721.11231.0
  • Codex core/CLI version recorded in the rollout: 0.146.0-alpha.3.1
  • Remote client reported by app-server: codex_chatgpt_android_remote
  • Remote client version reported by app-server: dev
  • Parent thread source: Realtime Voice
  • New-task target: projectless

What happened

  1. From Android Remote, I asked an existing Realtime Voice thread to create one separate projectless task.
  2. The parent rollout contains only one codex_app__create_thread tool call, with one call ID.
  3. That tool call returned one thread ID.
  4. However, the Windows host received seven thread/resume requests for the same parent thread and the same active turn, all on the same app-server connection.
  5. The same connection then issued seven relevant thread/start requests.
  6. Seven distinct top-level threads were persisted. They had the same delegated input and the same source parent, but different thread IDs, turn IDs, rollout files, and numbered projectless working directories.

Expected behavior

One codex_app__create_thread call should create exactly one new task. Resuming or reopening the active parent thread must not re-execute an already pending or completed side-effecting tool call.

Actual behavior

One recorded tool call resulted in seven real thread/start operations and seven persisted top-level tasks.

Sanitized host-log evidence

The values below are deliberately redacted. All seven resume requests referred to the same parent thread, active turn, client, and connection:

client_name="codex_chatgpt_android_remote"
client_version="dev"
connection_id=<SAME_CONNECTION>

thread/resume parent_thread=<PARENT_THREAD> active_turn=<ACTIVE_TURN> request_id=<R1>
thread/resume parent_thread=<PARENT_THREAD> active_turn=<ACTIVE_TURN> request_id=<R2>
thread/resume parent_thread=<PARENT_THREAD> active_turn=<ACTIVE_TURN> request_id=<R3>
thread/resume parent_thread=<PARENT_THREAD> active_turn=<ACTIVE_TURN> request_id=<R4>
thread/resume parent_thread=<PARENT_THREAD> active_turn=<ACTIVE_TURN> request_id=<R5>
thread/resume parent_thread=<PARENT_THREAD> active_turn=<ACTIVE_TURN> request_id=<R6>
thread/resume parent_thread=<PARENT_THREAD> active_turn=<ACTIVE_TURN> request_id=<R7>

The same remote connection issued seven new-thread requests during the incident window:

thread/start connection_id=<SAME_CONNECTION> client_name="codex_chatgpt_android_remote" request_id=<S1>
thread/start connection_id=<SAME_CONNECTION> client_name="codex_chatgpt_android_remote" request_id=<S2>
thread/start connection_id=<SAME_CONNECTION> client_name="codex_chatgpt_android_remote" request_id=<S3>
thread/start connection_id=<SAME_CONNECTION> client_name="codex_chatgpt_android_remote" request_id=<S4>
thread/start connection_id=<SAME_CONNECTION> client_name="codex_chatgpt_android_remote" request_id=<S5>
thread/start connection_id=<SAME_CONNECTION> client_name="codex_chatgpt_android_remote" request_id=<S6>
thread/start connection_id=<SAME_CONNECTION> client_name="codex_chatgpt_android_remote" request_id=<S7>

The parent rollout has only one creation call:

custom_tool_call name="exec" call_id=<ONE_CALL_ID>
input: tools.codex_app__create_thread({ prompt: <REDACTED>, target: { type: "projectless" } })

custom_tool_call_output call_id=<SAME_ONE_CALL_ID>
output: { "threadId": "<FIRST_CREATED_THREAD>" }

Suspected cause

The confirmed boundary is the Android Remote/app-server thread lifecycle, not model-level intentional delegation:

  • The model-side parent rollout recorded one creation tool call.
  • The Android Remote connection sent seven resumes and seven starts.
  • Seven independent threads were actually persisted.

The likely failure mode is that repeated thread/resume handling at the Remote application/event layer replayed or redispatched the pending side-effecting create_thread action. This is not evidence of raw TCP packet retransmission. The create-thread tool does not expose an idempotency key, and the receiving path did not deduplicate the repeated execution using the original tool call ID.

The logs establish the repeated request sequence and persisted effects. They do not expose enough closed-client implementation detail to identify the exact Android callback or event-replay code path.

Suggested fix

  • Treat the original tool call ID or a generated operation ID as an idempotency key for new-thread creation.
  • Do not redispatch pending/completed side-effecting tool actions when an active thread is resumed.
  • If the same create operation is received again, return the originally created thread ID instead of issuing another thread/start.
  • Add a regression test covering repeated thread/resume while a create_thread tool call is in progress.

Privacy note

This report intentionally omits:

  • account identifiers and email addresses;
  • Windows username and home directory;
  • project names and repository paths;
  • complete thread, turn, message, call, and installation IDs;
  • the original user prompt and transcript;
  • raw rollout files, logs_2.sqlite, and authentication/configuration files.

Sanitized excerpts can be provided privately if maintainers need request ordering or timestamps.

View original on GitHub ↗

4 Comments

github-actions[bot] contributor · 28 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #36250

Powered by Codex Action

sinan-0721 · 25 days ago

I found a second, separate Realtime Voice incident with the same one-call-to-many-persisted-tasks signature, on a later Codex core build.

Environment

  • Host: Windows Codex Desktop
  • Parent source: realtime_voice
  • Child source: subagent
  • Codex core/CLI: 0.146.0-alpha.9.2 (recorded directly in the parent rollout)
  • The same app-server process later reported Desktop version 26.727.51351
  • client_managed_handoffs: false

Observed correlation

All three cases occurred in the same Voice parent session:

| Recorded create_thread calls | Thread IDs returned by the tool | Additional same-turn resume responses | Persisted child tasks | Creation burst |
|---:|---:|---:|---:|---:|
| 1 | 1 | 10 | 11 | 1.476 s |
| 1 | 1 | 10 | 11 | 1.901 s |
| 1 | 1 | 9 | 10 | 1.806 s |

For each batch:

  • The parent rollout contains only one create_thread call ID and one tool output.
  • Every child had the same initial delegated input and the same direct source parent.
  • The children had distinct thread/turn IDs and rollout files, so these were real independent tasks rather than duplicate UI entries.
  • No recursive child spawning was found.
  • The number of additional resume responses exactly equals the number of surplus persisted tasks in all three cases.

I also observed a likely related delivery symptom: identical child-result payloads were delivered back to the Voice parent repeatedly. Across the three batches, 11, 10, and 11 unique result payloads appeared as 60, 67, and 60 parent messages respectively; a single payload arrived up to 7–8 times.

Evidence boundary: this build’s retained local logs do not expose per-request thread/start lines for this incident, so I can confirm repeated resume responses and persisted effects, but not directly map each resume to a specific start request. I also cannot identify the exact failing component from these artifacts.

I can provide sanitized UTC windows, call/turn identifiers, and narrow host-log excerpts privately if useful.

jaydenreu · 24 days ago

I second this. I've had the exact same issues repeatedly across multiple sessions.

mwalsh-retailpointplus · 20 days ago

I reproduced the same class of failure while a parent task was running on a Windows ChatGPT/Codex desktop host on 2026-08-07.

Important client-path clarification: the source task may have been opened from an iPhone around the time the duplicates were created. I cannot therefore characterize this as a confirmed desktop-only reproduction. The available task transcripts establish the duplicated execution, but they do not by themselves prove whether an iPhone Remote resume/reopen triggered the replay. This may be closely related to the Remote lifecycle described in the original report.

Observed behavior:

  • A parent task delegated one implementation job to a local project.
  • The parent transcript/final response referenced one intended created task.
  • The app task catalog contained three distinct persisted tasks carrying the same delegated input and the same source parent.
  • The three tasks started within approximately 45 seconds of one another and had separate thread/turn identities.
  • This was not duplicated rendering: all three tasks executed the work concurrently.
  • Their transcripts explicitly detected one another's concurrent file changes, builds, smoke-test processes, and staging-directory locks.
  • All three eventually completed, and no data loss has been observed, but the duplicate execution consumed unnecessary model usage/tokens and caused avoidable workspace contention.

Expected behavior: one delegation/create operation should persist and execute exactly one child task. Opening, resuming, or reconnecting to the parent from another client must not replay a pending or completed side-effecting task-creation operation. A repeated request should return the original child task through an idempotency key rather than starting another task.

For privacy, I am not posting the parent/child task IDs, repository name, paths, prompt, or raw transcripts publicly. Those identifiers can be supplied through an OpenAI-controlled private/in-app feedback channel if maintainers need to correlate telemetry.