Blocking stop hook continuation can fail with invalid local message id
Bug
A blocking stop hook can break the Codex turn instead of prompting Codex to fix the blocked condition.
Observed failure:
{
"error": {
"message": "Invalid 'input[118].id': 'c047686e-8eaf-4c21-9eba-e25d3122d898'. Expected an ID that begins with 'msg'.",
"type": "invalid_request_error",
"param": "input[118].id",
"code": "invalid_value"
}
}
Why this matters
A stop hook with decision: "block" is supposed to inject a continuation prompt, for example telling Codex to fix failing lint/type/test checks before stopping.
In this failure mode, the stop hook successfully blocks and emits the continuation prompt, but the next Responses API request rejects the conversation input. That makes the hook mechanism counterproductive: it prevents Codex from continuing to fix the problem.
Expected behavior
When a stop hook blocks, Codex should append the hook continuation prompt and continue the turn normally.
Actual behavior
Codex sends a locally generated UUID as input[].id for a message item. The Responses API rejects it because message ids must be server-generated msg... ids.
Root-cause hypothesis
build_hook_prompt_message creates a user message with a local UUID. Later, attach_item_ids reattaches hidden item ids into the serialized Responses API request body.
That path appears to treat local message UUIDs the same as server-generated message ids, so a local UUID is sent as a wire message.id.
Reference fix
I prepared a minimal fix in a fork.
Reference commit:
https://github.com/shawnsw/codex/commit/15286e639ad4f11771ddf1393b565f503c0aafc7
Approach:
- only reattach
message.idwhen it looks like a server-generatedmsg...id - continue preserving ids for other Responses API output item types
- add regression coverage for local UUIDs and server message ids
15 Comments
저도 이 오류를 계속 겪습니다. ms azure ai foundry에 배포된 gpt-5.5 모델은 response api로만 서빙되며, 이것을 연결해서 사용하는중에 자주 발생하고있어요. opencode + omo 에서는 이런오류를 겪지않는데말이죠.
Similar situation, I'm also using Azure AI Foundry, model gpt-5.5. Unsure if this is only reproducible with Foundry.
Indexed this hook ticket in the umbrella tracker: #21753
Goal: collect the scattered Codex hook requests and bugs into one parity matrix for Full Claude Code Hook Parity (29+), while preserving this issue as the detailed thread for its specific behavior.
I confirm I see the same issue when using the model from Azure AI Foundry.
Do you see the same with direct OpenAI model API? Haven't tested that myself.
I believe this has the same root cause as #27928: a local Codex-generated message ID is replayed into
input[].id, and Azure Responses rejects it because message IDs must begin withmsg.I verified a local patch that strips non-
msg*IDs only forResponseItem::Messagebefore Responses API requests, while preserving server message IDs and non-message item IDs.More details in #27928:
https://github.com/openai/codex/issues/27928#issuecomment-4817066485
My verified branch:
https://github.com/shawnsw/codex/tree/fix/responses-local-message-ids
@pakrym-oai this fix addresses both #20783 and #27928. Refrained from raising a PR per the repo guidelines, but happy to raise one if it would be accepted?
Additional local reproduction on Codex Desktop 26.707.31428 (CLI 0.144.0-alpha.4):
hook_promptafter a turn that edited files.messagewith a locally generated UUID inmessage.id, rather than a server-generatedmsg...ID.replacement_history.ApiIdParam/invalid_id_prefixbecause that local UUID was serialized asinput[6].id.The UUID is not the thread/conversation ID and the user did not supply it through an API request. This reproduces the failure from an ordinary Desktop session with a Stop hook enabled.
This supports guarding both persistence/replay paths: do not retain a local UUID as a wire
message.id; and only attach a message ID to a Responses request when it is a server-generatedmsg...ID. No local paths, thread IDs, or archived conversation contents are included here.Confirming this on the direct OpenAI path (stock provider config, ChatGPT-account auth — no Azure involved): a Stop hook returning
{"decision":"block","reason":"…"}permanently wedged a session in the Codex macOS app.What the session rollout shows: the block reason was recorded into history as a user
messageitem wrapped in<hook_prompt hook_run_id="stop:2:…">, carrying a locally generated bare UUIDv7id(019f4da5-72da-7671-b599-b3cd3b157b40— no type prefix). Every subsequent request replayed that item and was rejected:We saw the same rejection hours apart (
input[0].id, laterinput[97].id) as the conversation grew. The session never recovered and had to be abandoned — so beyond breaking the continuation turn (the OP's case), a Stop-hook block poisons persisted history: every later turn fails, which in practice is permanent session loss. Notably it is not deterministic: another long-running session on the same machine absorbed five identicalstophook injections (same<hook_prompt>shape, same bare-UUIDv7 ids in the rollout) without failing, including across compaction and resume — so the rejection appears to depend on when/whether the locally-generated id gets reattached on replay. We've disableddecision:blockon Codex entirely until this is fixed.Environment: macOS (arm64, Darwin 25.5.0), Codex macOS app;
codex --versionon the same machine reportscodex-cli 0.131.0; modelgpt-5.6-terra. #32282 looks like another manifestation of the same class.I can reproduce this in Codex Desktop on Windows, and in my case it is also triggered by a hook.
Hook feedback
[ApiIdParam] [input[0].id] [invalid_id_prefix]
Invalid 'input[0].id': '019f5eaa-4fa9-70d1-9d35-a6b2fbdcae81'.
Expected an ID that begins with 'msg'.
The rejected value is a locally generated UUID from the hook-generated message, not a server-generated msg... ID.
This appears consistent with the root cause described in this issue: a hook continuation message is persisted with a local UUID, and that UUID is later reattached as a Responses API input[].id.
Environment:
Product: Codex Desktop
Platform: Windows x64
Trigger: hook feedback / hook continuation
Authentication: ChatGPT subscription
I have not verified a fix or workaround yet. This report only confirms another reproduction of the same failure path.
Additional reproduction after the latest macOS desktop update:
{"decision":"block","reason":"..."}stop_hook_activeis true, the hook returns{"continue":true}ApiIdParam / invalid_id_prefixbecause a local UUID is sent asinput[].idwhere a server-generatedmsg...ID is requiredThe same invalid-ID class also appears in local desktop logs during
ambient_suggestionsrefresh, which suggests the item-ID serialization/replay problem is broader than only parsing the Stop hook result. The blocking Stop hook reliably exposes it by creating a continuation message.No local paths, thread IDs, full UUIDs, or conversation contents are included here.
Verified on 2026-07-14 against current main d7ba5ff9553a6aa0898a8e3bd5cb3bc00d0c9ddf and parent 08ba14b03d0 of #32312. A focused Azure/store request test added two hook-prompt user messages with the same synthetic bare UUID, representing persisted history and compaction replacement_history. At the parent, both IDs remained on the outbound request and the omission assertions failed (0 passed, 1 failed). On current main, the same test passes (1 passed, 2978 skipped): request preparation removes any response-item ID that is not prefixed before the store/item-IDs early return, covering legacy history on HTTP and WebSocket paths.
This confirms that the serializer/replay root cause reported here is resolved in source by #32312 (merge c9d52de5ca52a6b4439e7c0f69b34f6331926bb4); a duplicate patch or report is not needed. The installed Codex Desktop 26.707.71524 still bundles codex-cli 0.144.2 and reproduced the bare-UUID hook-prompt poison in a disposable task today. The remaining upstream delivery gap is a Desktop build that contains c9d52de5. Standalone CLI 0.144.4 resume can process the poisoned disposable history, but that does not repair the older Desktop app-server loaded context.
Same problem been happening to be , breaking a lot of long threads
019f6020-9870-7130-b4a7-19d4bfd377d7
Additional affected Codex Desktop session ID:
019f12d2-590c-7941-9c82-73869d5c7357.Confirming this is now reproducible in the new ChatGPT desktop app's Codex mode with a configured hook.
Observed behavior
This appears consistent with the Desktop regression tracked in #32282, but the hook firing is the direct trigger in this reproduction. Because hooks are part of the normal workflow, this is currently blocking practical use of the Desktop app rather than being an intermittent cosmetic error.
App/OS version and a sanitized hook definition can be added if maintainers identify the preferred diagnostics or log location.
I reproduced this issue twice in Codex Desktop on macOS.
Environment:
codex-cli 0.136.0.codex/hooks.jsonThe Stop hook returned a blocking decision:
Codex then stored the generated hook prompt as a local user message with a UUID-style id:
A later request failed with:
After this happens, every subsequent prompt in the same thread returns the same error, making the thread permanently unusable. A structured scan of local rollout logs found multiple Stop-hook-generated
hook_promptmessages whose message ids were UUIDs rather than server-generatedmsg_ids.As a project-side workaround, I changed the Stop hook from
decision:blockto advisorydecision:allowwhile preserving the review-required marker. I added a regression test that creates an isolated Git repository, triggers the real review marker and Stop hook, and verifies that the marker remains while the Stop decision isallow. The failure has not reproduced through that path since.This appears to be the same local-message-id /
attach_item_idsroot cause described here and is also related to #27928.