CLI /review follow-up fails with review_rollout_user id: Expected an ID that begins with 'msg'
What version of Codex CLI is running?
codex-cli 0.139.0
What subscription do you have?
Using an Azure OpenAI / Azure Foundry model provider.
Which model were you using?
gpt-5.5-1
What platform is your computer?
Darwin arm64 macOS 26.5.1
What terminal emulator and version are you using (if applicable)?
Ghostty 1.3.1, zsh
Codex doctor report
Not pasting the full report because it contains local paths and provider-specific resource names.
Relevant redacted excerpts:
{
"overallStatus": "ok",
"codexVersion": "0.139.0",
"config.load": {
"model provider": "azure",
"model": "gpt-5.5-1"
},
"network.websocket_reachability": {
"model provider": "azure",
"provider name": "Azure OpenAI",
"wire API": "responses",
"supports websockets": "false"
},
"updates.status": {
"latest version": "0.139.0",
"version status": "current version is not older"
}
}
What issue are you seeing?
Running /review can leave the session in a state where the next Codex request fails with a Responses API validation error:
{
"error": {
"message": "Invalid 'input[0].id': 'review_rollout_user'. Expected an ID that begins with 'msg'.",
"type": "invalid_request_error",
"param": "input[0].id",
"code": "invalid_value"
}
}
This appears related to the older /review issues that were closed after fixing the colon-containing id (review:rollout:user) in #9360 / #9131, and to #9860 which reported the same review_rollout_user / Expected an ID that begins with 'msg' error. The current failure is still reproducible on the latest published CLI version, 0.139.0.
From inspecting the current source, /review still materializes local message ids named review_rollout_user / review_rollout_assistant, and attach_item_ids later reattaches stored item ids into serialized Responses API message items. That means a local client-generated message id can be sent back as input[].id, while the API expects message ids to be server-generated ids beginning with msg.
A similar root cause is described in the still-open hook issue #20783, where a local UUID message id is sent back to the Responses API and rejected with the same Expected an ID that begins with 'msg' validation rule.
What steps can reproduce the bug?
- Configure Codex CLI 0.139.0 with an Azure OpenAI / Azure Foundry provider using the
responseswire API. - Open a repository.
- Run
/review. - After the review finishes, continue in the same session with a follow-up prompt.
- The next request can fail immediately with the validation error above.
What is the expected behavior?
The /review command should be able to roll its result back into the parent session and allow follow-up prompts without sending local client-generated message ids as Responses API input[].id values.
Additional information
The previous fix in #9387 appears to have addressed the older Azure validation failure caused by colons in review:rollout:user, but the current validation rule rejects review_rollout_user because message item ids are expected to begin with msg.
A minimal fix might be to preserve id for non-message Responses API output item types, but only reattach message.id when it looks like a server-generated message id, for example when it starts with msg. Locally generated message ids such as review_rollout_user, review_rollout_assistant, and UUIDs should not be serialized as Responses API message ids.
14 Comments
I reproduced the
/reviewfollow-up path in source and prepared a minimal patch locally.Root cause:
/reviewrecords parent-session rollout messages with local IDs such asreview_rollout_userandreview_rollout_assistant.store: true.storeis true, so those local message IDs can be serialized asinput[].id.msg, so it rejectsreview_rollout_user.Patch shape:
ResponseItem::Message.idwhen it does not start withmsg.msg...intact.review_history_surfaces_in_parent_sessionso it runs through the Azure/store path and asserts the review rollout messages are present but have no serializedid.Local validation:
cargo fmt -- --config imports_granularity=Item --checkpassed, with stable-rustfmt warnings thatimports_granularity=Itemis nightly-only.git diff --checkpassed.cargo check -p codex-core --libpassed.just test -p codex-core review_history_surfaces_in_parent_sessioncould not complete locally: the first run failed during initial Windows dependency build atcrossbeam-utilswithout a useful diagnostic, and the second single-job run exceeded a 15 minute timeout during first workspace build.If maintainers want an invited PR, I have a branch ready:
The same on the latest ChatGPT Desktop for Mac (Version 26.707.71524). The thread couldn't be used anymore, as the error comes with every single message:
The issue occurred after the code review started, and then a message was sent from the iOS app to the thread.
Reported with the feedback (019f56e9-9a75-7ed0-8c18-9b99ac2ce57b).
26.707.71524 Win11 desktop. Seeing same issue as pvalentij.
Manually editing all sessions fixed the issue for a few rounds of chats, but then got new corrupted messages: a hook feedback item gets a UUIDv7 id → invalid_id_prefix at input[0].
Extra details from digging through my rollout files:
---
Update — root cause narrowed down: the corruption comes from the stop-hook follow-up prompt injection path. I classified every bad id in my session history (48 distinct UUIDv7s): all 48 are hook-injected messages (
<hook_prompt hook_run_id="stop:...">...), zero are normal typed messages. The injected item is persisted with the app's internal UUIDv7 as itsid; normal user messages are stored with noidat all.It's a fresh regression, not a config issue: my hook script is unchanged since June 1 and ran fine for 5+ weeks, and every bad id is minted after 2026-07-09 19:00 UTC (UUIDv7 timestamps) — matching the early-July builds that reworked hook context/trust handling. Microsoft Store install, auto-updated, so the regression shipped in whatever build rolled out around July 8–9.
Workaround confirmed: disabling the prompt-injecting stop hook stops new corruption entirely (plus the recursive id scrub above for already-poisoned threads).
This is not Azure-specific, and it reproduces on OpenAI's own first-party subscriptions — including the latest 0.144.5.
The "please report this to Azure" response on the earlier issues (#9360) does not hold up. In this very thread, @pavlentij reproduced the exact same
review_rollout_userid on ChatGPT Desktop for Mac (26.707.71524) — a first-party client, no Azure involved. #33176 shows the sameExpected an ID that begins with 'msg'on a PRO 20x subscription. So this is not a "temporary bug in Azure's endpoint" — it is a client-side bug in how Codex serializes locally-generated message ids back into the Responses API.The root cause has been stated plainly and repeatedly:
/review(and the hook-injection path) mint local message ids likereview_rollout_user/ UUIDv7, andattach_item_idssends them back asinput[].id, which the Responses API rejects because message ids must be server-generated (msg…). The:→_normalization in #9387 only swapped one validation failure for another.This has now been open and reproducible across 0.114 → 0.144.5 (the current latest) and multiple first-party app builds, with a proposed patch shape already posted (clear
ResponseItem::Message.idwhen it does not start withmsg). Deflecting first-party reproductions to "go tell Azure" is not an acceptable resolution. Please pick up the fix.When this issue will be fixed? Its been really irritating
Having the same issue with Codex MacOs here. The terminal is ok for me though.
I can reproduce this on
codex-cli 0.146.0, so the issue remains present in a newer release than the originally reported0.139.0.After using
/reviewseveral times in a long-running session, every subsequent prompt fails with:Changing the model and reasoning effort does not help. Remote compaction also fails with the same validation error:
Inspection of the local rollout JSONL confirms that it contains persisted message items using both synthetic IDs:
review_rollout_userreview_rollout_assistantNormal message items in the same rollout use server-style
msg_...IDs. This supports the root-cause analysis in the issue: review messages are persisted with client-generated IDs, andattach_item_idslater serializes those IDs into Responses APIinput[].id.Because the invalid IDs are persisted, restarting Codex or switching models does not recover the session. Compaction cannot recover it either because compaction replays the same malformed item.
Expected behavior: Codex should only attach message IDs that are valid server-generated IDs, such as IDs beginning with
msg, and should omit locally generated review IDs when serializing Responses API input. Existing rollouts containing these synthetic IDs should ideally be sanitized during replay so affected sessions can be recovered.Bump into the same issue,
codex-cli 0.146.0, MacOSThanks for the additional confirmations. The reports from
codex-cli 0.146.0and ChatGPT Desktop on both macOS and Windows show that this is not Azure-specific and is still present in current first-party clients.I prepared an earlier minimal patch in [dbca2ed](https://github.com/He-wei-gui/codex/commit/dbca2ed7b5797c80e528aa1c95fb2d970293c8dc). During Responses request preparation, it removes
ResponseItem::Message.idvalues that do not start withmsg, while preserving valid server-generated message IDs and IDs belonging to non-message response items.The later reports about hook-injected UUIDv7 message IDs appear to confirm that the same validation should cover both
/reviewrollout messages and hook follow-up messages. Applying the guard during request preparation should also prevent already-persisted invalid message IDs from being replayed to the API, allowing affected sessions to work again without manually editing rollout files.Maintainers, if this direction is acceptable, I can rebase the branch onto the current
mainbranch and prepare an invited PR with regression coverage for:/reviewsynthetic message IDsWould you like me to proceed with an invited PR?
Confirmed on Linux with OpenAI first-party authentication:
After upgrading to 0.146.0, resuming an existing long-running session containing /review results caused every subsequent prompt to fail with:
The session JSONL remained intact. I found 12 local sessions containing the review_rollout_user message ID. The failure persisted across retries because the invalid ID was replayed from saved and compacted history.
This confirms the issue is also reproducible on Ubuntu/Linux and is not platform-specific. I downgraded to 0.145.0 as a temporary workaround.
I reproduced this on
codex-cli 0.146.0by resuming a legacy0.144.6rollout whose compactedreplacement_historycontains message items with:review_rollout_userreview_rollout_assistantThe exact failure is:
Root cause: #32312 added request-time filtering for unprefixed legacy response item IDs, but
ResponseItemId::is_prefixed()currently accepts any non-empty text on both sides of the first underscore. Thereforereview_rollout_useris considered “prefixed” even though aResponseItem::Messagespecifically requires themsgprefix. The incompatible ID survives request preparation and is replayed to the Responses API.I prepared a focused fix against current
main:ResponseItem::id_prefix()mapping.Branch: https://github.com/warproxxx/codex/tree/fix/legacy-review-message-id-prefix
Commit: https://github.com/warproxxx/codex/commit/7ed1e6a6bc70ff0778c575eba7f54d8998ae3f32
Validation:
review_rollout_*IDs present in outbound JSON.just test -p codex-protocol: 268 passed.just test -p codex-core: 3,184 passed; the two remaining failures are unrelated macOS sandbox/path cases (apply_patch_cli_does_not_write_through_symlink_escape_outside_workspaceandopenpty_works_under_real_exec_seatbelt_path).just fix -p codex-protocol,just fix -p codex-core, andjust fmtcompleted.This also covers the Codex App report in #34283. Per the repository's invitation-only contribution policy, I have not opened an unsolicited PR. If a maintainer confirms this approach and explicitly invites the contribution, I can open the ready branch immediately.
This looks like a useful persisted item-ID/replay case. I’m field-testing
codex-rescue==0.1.0a4against histories where locally generated tool/review state can poison a later request.If you still have the affected rollout, could you run:
pipx install codex-rescue==0.1.0a4codex-rescue doctor --json "/path/to/the-affected-rollout.jsonl"It won’t fix Azure/API serialization, but the pass is read-only and can help determine whether the bad/local message identity is present in durable session history versus being introduced only during request reconstruction.
Please share only sanitized status/findings/aggregate counts—no raw rollout, DB, provider config, prompts, tool output, credentials, repository data, session IDs, or private paths.
still buggy in 0.149.1..
have to downgraded back to 0.145.0....
To add a practical recovery path for anyone stuck with
Invalid 'input[...].id': 'review_rollout_user'. Expected an ID that begins with 'msg':Because the synthetic
review_rollout_*item IDs are persisted in the session JSONL, switching models or restarting the CLI cannot recover the session on its own.Instead of manually editing JSONL files or downgrading CLI versions, you can inspect and create a sanitized recovery snapshot using Vetto:
The recovery engine runs read-only with bounded budgets and strips locally generated review artifact identifiers before serializing clean replay state.