`codex exec resume` fails with 'Missing required parameter: input[N].encrypted_content' after internal 'thread not found' (rollout intact)
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 theResponsesApiRequest.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 thethread <id> not foundERROR) — check whether the same lookup miss also feeds the input-builder downstream.- Whatever code transforms loaded rollout entries into
ResponsesApiRequest.inputfor the resume case — verify it preservesencrypted_contentonReasoningitems 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.
5 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Severity update — this is broader than
resume.Originally I framed this as a
codex exec resumebug. Further investigation today shows the sameMissing required parameter: 'input[N].encrypted_content'error fires on plain new multi-turn conversations, not just resumed ones. Thethread not foundlog line is a non-fatal red herring — sessions are correctly registered instate_5.sqlite(verified the failing session IDs are present inthreads). The single-turn case completes successfully despite that log; the failure surfaces only onceinput[]includes a previous reasoning item.Today's evidence (codex-cli 0.125.0, ChatGPT Pro via local gateway,
wire_api = "responses")Failure case — paseo agent driving
codex app-server, sessionId019dc93d-8c4e-77c1-9014-c95e12a6d252, fresh new conversation (no resume), failed mid-conversation:Index is now
input[5], notinput[3]— i.e. several turns went through fine before the bug fired. Session ran ~1 hour with multiple reasoning + tool turns before failing.Rollout audit for that session (337 items, all reasoning items): 0 reasoning items missing
encrypted_content. Lengths 996–21516 bytes. The on-disk persistence is fine. The drop happens between rollout and request-body construction.state_5.sqliteaudit:threadstable contains 723 records including all the failing session IDs, with correctrollout_path,cli_version,model,model_provider. Thread index is healthy. So:resume(also fires on fresh new conversations)Reasoningitems intoResponsesApiRequest.inputwithout theirencrypted_contentfor some subsetUpdated reproduction
Any new multi-turn conversation with a reasoning model + responses wire-api eventually fires this. Single-turn
codex exec "ping"succeeds. The originalcodex exec resumerepro still works (and is the most deterministic), but the failure mode is more general.Suggested re-triage
Severity should be higher than initially scoped — this affects every multi-turn user on
responseswire-api with reasoning models, not just users whoresume. Workaround for users in the meantime: keep sessions to 1–3 turns and start fresh. No reliable client-side workaround for longer sessions.Happy to provide rollout files, sqlite dumps, or anything else useful.
Adding another surface of this on
0.125.0.I see the same
failed to record rollout items: thread <uuid> not foundlog line, butcodex exec resumeexits 0 with a freshthread.startedevent in the JSONL — i.e., resume silently starts a new session instead of erroring loudly. No HTTP 400 surfaces to the user.Repro (gstack
/codexskill consult-resume path):stderr both runs:
Environment: macOS 14 arm64, codex-cli 0.125.0 (Homebrew cask), ChatGPT auth (no MCP), no profiles, same
model_reasoning_effort="medium"between sessions. Rollout file on disk for the original session contains a full agent_message + thread.started — should be resumable.User impact for tools wrapping codex: Wrappers that don't compare
thread.started.thread_idbetween calls assume resume succeeded — they get conversational continuity silently dropped. (gstack /codex consult-resume mode hit this twice in one session.)Hope it helps narrow the codepath. Happy to share the rollout files if useful.
This is a useful control/parser case for
codex-rescue: your audit says the rollout itself is intact and theencrypted_contentloss happens later during request construction. I’m field-testing whether Rescue can inspect that persisted session without falsely labeling it corrupted or trying to reconstruct provider-specific state.If you still have an affected local session, could you run:
A healthy/unsupported result is useful evidence here.
codex-rescuedoes not claim to fix Responses request serialization or missing encrypted reasoning fields; source rollouts remain read-only.Please share only sanitized output plus versions/exit codes. No raw rollout/SQLite, encrypted state, prompts, credentials, or private paths. Repo: https://github.com/shleder/codex-rescue
Your table is the right way to slice this — the distinguishing detail here is that the rollout is intact on disk but the server-side thread lookup fails first ("thread not found"), and then the replayed input is rejected for missing
encrypted_content. That ordering matters: it means the failure isn't that the encrypted field can't be decrypted (as in #15219/#17541), it's that the resume path is reconstructing input items without their encrypted_content because the thread context it would pull them from wasn't found.So the root cause is upstream of the API validation: the local→server thread binding is broken (self-hosted gateway forwarding to backend-api can lose or not propagate the thread identity), and the missing encrypted_content is a downstream symptom. Confirming whether the rollout's encrypted_content fields are present-but-unused vs genuinely absent is the diagnostic that separates "gateway dropped the thread binding" from "rollout was written without encryption."
Full disclosure: I maintain codex-rescue (https://github.com/shleder/codex-rescue).
codex-rescue doctor --latestinspects rollout record integrity including whether encrypted_content fields are present on the items that require them, read-only, so you can tell whether your rollout is complete and the problem is purely the gateway's thread binding. That narrows the fix to the forwarding layer instead of the session file.