`codex exec resume` fails with 'Missing required parameter: input[N].encrypted_content' after internal 'thread not found' (rollout intact)

Open 💬 5 comments Opened Apr 26, 2026 by lowmiaq-gmail
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

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:

  1. 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.
  2. 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.

View original on GitHub ↗

5 Comments

github-actions[bot] contributor · 4 months ago

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

  • #19532

Powered by Codex Action

lowmiaq-gmail · 4 months ago

Severity update — this is broader than resume.

Originally I framed this as a codex exec resume bug. Further investigation today shows the same Missing required parameter: 'input[N].encrypted_content' error fires on plain new multi-turn conversations, not just resumed ones. The thread not found log line is a non-fatal red herring — sessions are correctly registered in state_5.sqlite (verified the failing session IDs are present in threads). The single-turn case completes successfully despite that log; the failure surfaces only once input[] 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, sessionId 019dc93d-8c4e-77c1-9014-c95e12a6d252, fresh new conversation (no resume), failed mid-conversation:

Missing required parameter: 'input[5].encrypted_content'

Index is now input[5], not input[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.sqlite audit: threads table contains 723 records including all the failing session IDs, with correct rollout_path, cli_version, model, model_provider. Thread index is healthy. So:

  • ❌ Not a "thread not found" / index-corruption issue (state is fine)
  • ❌ Not a rollout persistence issue (all reasoning items have EC on disk)
  • ❌ Not specific to resume (also fires on fresh new conversations)
  • ✅ Reproducible on every multi-turn session past some number of reasoning turns
  • ✅ Pure client-side construction bug — codex-cli emits Reasoning items into ResponsesApiRequest.input without their encrypted_content for some subset

Updated reproduction

Any new multi-turn conversation with a reasoning model + responses wire-api eventually fires this. Single-turn codex exec "ping" succeeds. The original codex exec resume repro 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 responses wire-api with reasoning models, not just users who resume. 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.

jfcampbl · 4 months ago

Adding another surface of this on 0.125.0.

I see the same failed to record rollout items: thread <uuid> not found log line, but codex exec resume exits 0 with a fresh thread.started event in the JSONL — i.e., resume silently starts a new session instead of erroring loudly. No HTTP 400 surfaces to the user.

Repro (gstack /codex skill consult-resume path):

# 1. Start a fresh session, save the thread_id from thread.started event
codex exec "first prompt" --json -c 'sandbox_mode="read-only"' > session1.jsonl
SESSION=$(jq -r 'select(.type=="thread.started") | .thread_id' session1.jsonl)

# 2. Try to resume — exits 0 but is actually a new session
codex exec resume "$SESSION" "second prompt" --json -c 'sandbox_mode="read-only"' \
  > session2.jsonl 2> session2.err
echo "exit: $?"  # 0
grep 'thread.started' session2.jsonl  # NEW thread_id, not the resumed one
grep 'thread not found' session2.err  # the symptom

stderr both runs:

2026-04-28T06:59:31Z ERROR rmcp::transport::worker: worker quit with fatal: Transport channel closed, when Auth(TokenRefreshFailed("Server returned error response: invalid_grant: Invalid refresh token"))
2026-04-28T07:00:42Z ERROR codex_core::session: failed to record rollout items: thread 019dd2db-1e8b-7842-b58b-8d1177074805 not found

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_id between 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.

shleder · 11 days ago

This is a useful control/parser case for codex-rescue: your audit says the rollout itself is intact and the encrypted_content loss 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:

pipx install codex-rescue
codex-rescue sessions
codex-rescue doctor --latest

A healthy/unsupported result is useful evidence here. codex-rescue does 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

shleder · 6 days ago

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 --latest inspects 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.