Skip to content

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

@lowmiaq-gmail

Description

@lowmiaq-gmail

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsessionIssues involving session (thread) management, resuming, forking, naming, archiving

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions