openai / openai/codex

app-server: thread history projection stalls forever after a duplicated rollout ordinal ("expected N, got N-1"); thread/items/list and thread/turns/list stop reflecting new turns

Open
#46,316 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app-server bug CLI
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

Environment

  • codex-cli 0.153.4 (standalone, x86_64-apple-darwin), app-server started with codex app-server --remote-control --listen unix://…
  • macOS (Darwin 25.5.0)
  • Stores: ~/.codex/state_5.sqlite, ~/.codex/thread_history_1.sqlite, rollouts under ~/.codex/sessions/

Summary

For some threads, the rollout file (sessions/**/rollout-*.jsonl) contains two consecutive lines with the same ordinal. The thread-history projection (codex_thread_store::local::live_writer) then fails on every append with

failed to project durable rollout for <thread>: thread-store internal error:
thread history projection for <thread> expected ordinal 5126, got 5125

and never advances again. turn/start still works and the rollout keeps growing (turns complete normally), but thread/items/list and thread/turns/list keep returning the state as of the duplicate. Any client that reads threads through the app-server (our CLI wrapper, our desktop app) sees the thread frozen days back, while the agent is actually working and finishing turns.

Observed (3 threads on one machine)

thread rollout lines projection next_rollout_ordinal first failure failures logged
01a09ec8-97ab-7a63-a27b-65c1a5d54a39 8194 (304 MB) 5126 2026-09-16 18:03 JST 367
01a0930e-6c16-7960-a3f1-8ab0f52a65d1 25554 (1.02 GB) 21892 2026-09-16 10:29 JST 442
01a094d7-f722-70c1-acd6-0aee5263d24b 15577 6560 2026-09-16 10:29 JST 414

Duplicated ordinals found by scanning the rollouts (line number, ordinal, record):

  • 01a09ec8: line 5127, ordinal 5125 = event_msg/task_started (2026-09-16T02:14:37Z) — the previous line also has ordinal 5125
  • 01a0930e: line 21893, ordinal 21891 = event_msg/thread_settings_applied (2026-09-16T00:49:17Z); and again line 22156, ordinal 22153 = event_msg/thread_settings_applied (2026-09-16T01:36:01Z)

No gaps, only duplicates; from the duplicate onward every ordinal is one behind the line index. In all cases the duplicate is the first record written when the thread was resumed (task_started / thread_settings_applied), which suggests the resumed session's ordinal counter starts at the last written ordinal instead of last+1.

thread_history_projection_state.next_rollout_byte_offset points exactly at the duplicated line, and next_rollout_ordinal is one more than that line's ordinal, so the projection is consistent with the file up to the duplicate and cannot pass it.

Expected

  • The writer should never reuse an ordinal on resume.
  • The projection should tolerate (skip or renumber) a duplicated ordinal instead of failing every append forever; at minimum it should surface the stall to API clients (e.g. thread/read reporting a projection error) rather than silently returning stale lists.

Impact

Remote-control clients cannot see new user messages or agent replies on the affected threads; delivery verification by reading thread/items/list fails although the turn ran to completion.

Repro idea

Resume a thread (notLoaded → thread/resume) and start a turn; check whether the first appended rollout record repeats the previous ordinal. We have not isolated the exact trigger; the three affected threads were all resumed on 2026-09-16 09:49–11:14 JST, two of them around an app-server restart at 11:13.

Logs

From logs_2.sqlite, target codex_thread_store::local::live_writer, e.g. 2026-09-18 07:14:49 JST:
persist_rollout_items{item_count=1}:append_items{item_count=1}: failed to project durable rollout for 01a0930e-…: thread-store internal error: thread history projection for 01a0930e-… expected ordinal 21892, got 21891
Also on shutdown: failed to project durable rollout during shutdown for 01a09ec8-…: … expected ordinal 5126, got 5125

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with codex_thread_store::local::live_writer and the thread_history_projection_state handling, then reproduce by resuming a thread and starting a turn. Inspect how the rollout byte offset and next ordinal behave at the duplicated record. Done means the projection advances without repeated failures and thread/items/list and thread/turns/list reflect subsequent turns.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust, sqlite
Domain
api, backend, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.