What version of Codex CLI is running?
0.125.0
What subscription do you have?
ChatGPT Pro (via self-hosted local gateway forwarding to chatgpt.com/backend-api; wire_api = "responses")
Which model were you using?
gpt-5.4
What platform is your computer?
macOS 14 (Darwin 25.3.0) arm64
What terminal emulator and version are you using (if applicable)?
Ghostty / iTerm2; also reproduces from paseo (third-party MCP host that drives codex app-server).
What issue are you seeing?
Resuming a previously-saved session fails immediately with:
{
"error": {
"message": "Missing required parameter: 'input[3].encrypted_content'.",
"type": "invalid_request_error",
"param": "input[3].encrypted_content",
"code": "missing_required_parameter"
}
}
…and is accompanied by this internal CLI log right after the failure:
ERROR codex_core::session: failed to record rollout items: thread 019dc8d9-3ff9-7f23-bb45-54818d3b3d9c not found
This is distinct from the related encrypted-content issues already on file:
| Issue |
Trigger |
Server error code |
Field state |
| #15219 (CLOSED) |
resume across model_provider profiles |
invalid_encrypted_content |
field present, can't decrypt |
| #17541 |
mid-conversation turn/start model switch |
invalid_encrypted_content |
field present, can't decrypt |
| #10506 |
(unclear) |
string too long on input[N].encrypted_content |
field present, malformed |
| This issue |
resume of own session, same provider throughout, rollout file intact |
missing_required_parameter |
field absent from request |
The rollout file on disk is fully healthy — every reasoning item has encrypted_content. The bug is that the CLI's resume reconstruction path omits the field when re-emitting input[] after the internal thread not found condition fires.
What steps can reproduce the bug?
Minimal reproduction (no paseo involved — bare codex exec resume):
codex exec --skip-git-repo-check resume <session-uuid> "Reply with PONG and stop."
For me, any session whose internal thread index entry has gone missing reproduces 100% on the first turn, regardless of model, prompt, or working directory. In my case the trigger appears to be sessions originally created by paseo calling codex app-server — those rollouts get written but no matching thread index entry seems to be created (or is later evicted), so subsequent codex exec resume of the same sessionId always trips this.
What is the expected behavior?
Either:
- Preferred: When the thread index lookup misses, hydrate the missing reasoning items (including
encrypted_content) from the rollout file before constructing the ResponsesApiRequest.input. Resume then succeeds.
- Fallback: If hydration is impossible, refuse to send a malformed request — surface a clear "thread index missing, cannot resume" error instead of forwarding a request guaranteed to be rejected by the server.
Today the request is built with the field omitted and sent anyway, producing a 400 every turn.
Additional information
Evidence the rollout file is healthy
For the failing sessionId 019dc8d9-3ff9-7f23-bb45-54818d3b3d9c, I audited every reasoning item in the on-disk rollout (~/.codex/sessions/2026/04/26/rollout-2026-04-26T16-12-54-019dc8d9-...jsonl, 1.1 MB, 504 lines):
reasoning items: 44
with encrypted_content: 44 (length min/max: 996 / 21516 bytes)
without encrypted_content: 0
So the persistence layer is fine. The drop happens between rollout and request body construction, in the resume path.
Why I think it's the "thread not found" fallback
The error log
ERROR codex_core::session: failed to record rollout items: thread 019dc8d9-... not found
appears immediately before the server-side rejection in stderr, every time. Same sessionId works perfectly the first time it's used (before whatever indexing condition trips); only resume of a session whose internal thread record is missing reproduces.
This looks like a sibling of #16271 (orphaned threads → crash/resume loop) and #16872 (app-server rollout never materialized → "no rollout found"), but inverted: in those issues thread-record exists / rollout missing; here rollout exists / thread-record missing. Both stem from the dual-storage (thread index + rollout file) drifting out of sync, but the consequence in this code path is silently dropping encrypted_content rather than raising.
Workaround
Don't codex exec resume affected sessions. Start a new session and feed the prior context in as a prompt. (Confirmed clean — a fresh sessionId with the same model/provider/CLI version works, since reconstruction is not invoked.)
Suggested investigation pointers
codex_core::session::record_rollout_items (the function emitting the thread <id> not found ERROR) — check whether the same lookup miss also feeds the input-builder downstream.
- Whatever code transforms loaded rollout entries into
ResponsesApiRequest.input for the resume case — verify it preserves encrypted_content on Reasoning items unconditionally rather than only when the thread index hit succeeds.
- Confirm whether sessions created via
codex app-server (no interactive UI) consistently register thread records, or whether the registration is tied to interactive resume / app paths.
Happy to provide rollout file excerpts or run additional probes if useful.
What version of Codex CLI is running?
0.125.0
What subscription do you have?
ChatGPT Pro (via self-hosted local gateway forwarding to
chatgpt.com/backend-api;wire_api = "responses")Which model were you using?
gpt-5.4
What platform is your computer?
macOS 14 (Darwin 25.3.0) arm64
What terminal emulator and version are you using (if applicable)?
Ghostty / iTerm2; also reproduces from
paseo(third-party MCP host that drivescodex app-server).What issue are you seeing?
Resuming a previously-saved session fails immediately with:
{ "error": { "message": "Missing required parameter: 'input[3].encrypted_content'.", "type": "invalid_request_error", "param": "input[3].encrypted_content", "code": "missing_required_parameter" } }…and is accompanied by this internal CLI log right after the failure:
This is distinct from the related encrypted-content issues already on file:
model_providerprofilesinvalid_encrypted_contentturn/startmodel switchinvalid_encrypted_contentstring too longoninput[N].encrypted_contentmissing_required_parameterThe rollout file on disk is fully healthy — every reasoning item has
encrypted_content. The bug is that the CLI's resume reconstruction path omits the field when re-emittinginput[]after the internalthread not foundcondition fires.What steps can reproduce the bug?
Minimal reproduction (no
paseoinvolved — barecodex exec resume):For me, any session whose internal thread index entry has gone missing reproduces 100% on the first turn, regardless of model, prompt, or working directory. In my case the trigger appears to be sessions originally created by
paseocallingcodex app-server— those rollouts get written but no matching thread index entry seems to be created (or is later evicted), so subsequentcodex exec resumeof the samesessionIdalways trips this.What is the expected behavior?
Either:
encrypted_content) from the rollout file before constructing theResponsesApiRequest.input. Resume then succeeds.Today the request is built with the field omitted and sent anyway, producing a 400 every turn.
Additional information
Evidence the rollout file is healthy
For the failing
sessionId 019dc8d9-3ff9-7f23-bb45-54818d3b3d9c, I audited every reasoning item in the on-disk rollout (~/.codex/sessions/2026/04/26/rollout-2026-04-26T16-12-54-019dc8d9-...jsonl, 1.1 MB, 504 lines):So the persistence layer is fine. The drop happens between rollout and request body construction, in the resume path.
Why I think it's the "thread not found" fallback
The error log
appears immediately before the server-side rejection in stderr, every time. Same
sessionIdworks perfectly the first time it's used (before whatever indexing condition trips); only resume of a session whose internal thread record is missing reproduces.This looks like a sibling of #16271 (orphaned threads → crash/resume loop) and #16872 (app-server rollout never materialized → "no rollout found"), but inverted: in those issues thread-record exists / rollout missing; here rollout exists / thread-record missing. Both stem from the dual-storage (thread index + rollout file) drifting out of sync, but the consequence in this code path is silently dropping
encrypted_contentrather than raising.Workaround
Don't
codex exec resumeaffected sessions. Start a new session and feed the prior context in as a prompt. (Confirmed clean — a freshsessionIdwith the same model/provider/CLI version works, since reconstruction is not invoked.)Suggested investigation pointers
codex_core::session::record_rollout_items(the function emitting thethread <id> not foundERROR) — check whether the same lookup miss also feeds the input-builder downstream.ResponsesApiRequest.inputfor the resume case — verify it preservesencrypted_contentonReasoningitems unconditionally rather than only when the thread index hit succeeds.codex app-server(no interactive UI) consistently register thread records, or whether the registration is tied to interactive resume / app paths.Happy to provide rollout file excerpts or run additional probes if useful.