Skip to content

Codex “Responses WebSocket messages must have type”: Router Fix

Older codex-router builds reject the response.interrupt Codex sends mid-turn. Update the checkout to main; as of October 22, 2026, Homebrew still ships v0.6.0.

••8 min read•OpenAI Codex
Codex sends response.create and response.interrupt to codex-router at 127.0.0.1:4202; the create passes to OpenAI, the interrupt is rejected with a 400 that returns to Codex, fixed in PR #1004

Responses WebSocket messages must have type response.create. does not come from OpenAI's servers in any traced report. It is written by a local Responses proxy that sits between Codex and the model. In every case traced so far, that proxy is codex-router, a third-party router that people install to use non-OpenAI models inside Codex.

Older router builds accept only one kind of WebSocket message, response.create. When you send a follow-up, answer a question card, or steer while a turn is still running, Codex sends a second message on the same connection, response.interrupt. The router answers it with HTTP 400 and the turn stops.

The router fix is PR #1004, merged on October 12, 2026. What you do next depends on how the router got onto your machine:

What you findWhat to do
openai_base_url points to 127.0.0.1:4202, router installed with the one-line installerUpdate the checkout, run doctor --fix, fully restart Codex
Router installed with Homebrewbrew upgrade still installs v0.6.0 (as of October 22, 2026), which predates the fix; use the workaround or disable the router for now
You need mid-turn follow-ups to stop failing today and cannot updateTry instant_interrupt = false as a temporary, untested workaround
No local proxy or custom base URL in your Codex configYour case is outside this explanation; report it on openai/codex #52221

Why “must have type response.create” only appears mid-turn

Codex talks to the Responses API over a WebSocket: one long-lived connection on which the client sends small JSON messages (often called frames), each with a type field. A turn starts with response.create, the turn-start event documented in OpenAI's WebSocket Mode guide.

The current Codex client does one more thing. While a response is still streaming and you add input, Codex can cut that response short by sending this on the same socket (responses_websocket.rs):

json
{"type":"response.interrupt","response_id":"…","mode":"discard_partial_items"}

It then waits for response.interrupt.accepted or response.interrupt.failed. This behavior sits behind a Codex feature flag, instant_interrupt, which is marked as under development and enabled by default (features/src/lib.rs). Follow-ups, question-card replies, and steering are exactly the inputs that trigger it.

codex-router's WebSocket bridge, up to release v0.6.0 and on main before October 12, checked every incoming message and refused anything that was not response.create (issue #1019). The refusal is the full error Codex shows:

json
{"type":"error","status":400,"error":{"type":"invalid_request_error","message":"Responses WebSocket messages must have type response.create."}}

The first message of a turn passes. Only the extra message sent while Codex is working gets rejected. That is why the error typically appears right after new input during an active turn, whether in Codex Desktop on macOS or Windows, the CLI, or Codex Remote viewed on an iPhone (#52221). The same guard also exists in some forks of the router, so a local proxy with a different name can print the identical string.

Side-by-side message sequence: with codex-router v0.6.0, response.create passes and response.interrupt gets 400 invalid_request_error, so the turn stops; after PR #1004 on main, the router answers response.incomplete with reason interrupted and the next response.create uses the same socket

“I only use GPT-6.1 Sol, why is a router involved?”

The router's Codex integration writes a managed block into ~/.codex/config.toml that sets openai_base_url to http://127.0.0.1:4202/_codex-router/<generated-capability>/v1 (older setups show http://127.0.0.1:4202/v1 ). See the router README. openai_base_url redirects Codex's built-in OpenAI provider, so native GPT turns also travel through the router, not only the external models you added. The reports in #52221 match this: the people who hit the error on gpt-6.1-sol and then checked their config found openai_base_url pointing at 127.0.0.1:4202.

Check whether a local router sits in Codex's path

Run these checks on the computer that runs the Codex turn. For Codex Remote, that is the Mac host, not the phone.

1. Look for a local base URL in your Codex config. On macOS or Linux:

sh
grep -n -E 'openai_base_url|base_url|codex-router' ~/.codex/config.toml \
  | sed -E 's#_codex-router/[^/"]+#_codex-router/…#'

On Windows, in PowerShell:

powershell
Select-String -Path "$HOME\.codex\config.toml" -Pattern 'openai_base_url|base_url|codex-router' |
  ForEach-Object { $_.Line -replace '_codex-router/[^/"]+', '_codex-router/…' }

The second half of each command masks the generated path segment. That segment works as a local password for the router, so never paste the full URL into an issue, a chat, or a screenshot.

What the output showsWhat it means
openai_base_url = "http://127.0.0.1:4202/…/v1" inside # BEGIN codex-router-managedcodex-router handles every turn, native GPT models included
Another localhost or LAN address in openai_base_url or a [model_providers.*] tableA different local proxy or gateway is in the path; check whether it accepts response.interrupt
No openai_base_url and no custom provider in useNo local proxy in the config; skip to the reporting section below

2. Confirm that Codex sends interrupt messages. Run codex features list and find the instant_interrupt row. If it is enabled, Codex cuts in on a running response whenever you add input.

3. Check whether your router build has the fix. The error text itself tells you. A message ending in must have type response.create. means the router process that answered was built before the fix. A fixed router accepts response.interrupt, and for any other unsupported message type its text changes to Responses WebSocket messages must have type response.create or response.interrupt. with the code unsupported_websocket_event (source).

For an installer-based setup in the default macOS/Linux location, check the code on disk:

sh
grep -c "response.create or response.interrupt" ~/.local/share/codex-router/src/responses-websocket.mjs

0 means the checkout predates the fix. 1 or more means the fixed code is on disk, but a router process started before the update keeps running the old code until it restarts. For Homebrew, brew info codex-router showing 0.6.0 means you have the unfixed build.

Fix it by updating codex-router to a build with PR #1004

PR #1004, "fix: handle Codex response.interrupt on persistent WebSockets," handles the interrupt outside the generation queue. It aborts the local request, ends the response with response.incomplete and reason interrupted, and keeps the socket open for the next response.create. Two limits apply:

  • It cancels the local HTTP request. It does not guarantee that a remote provider stops billing for a generation it has already started.
  • The PR's validation was 56 passing WebSocket tests, without a live generation. One Windows user backported it and a real question-card reply during an active desktop turn then succeeded (October 15, 2026, in #52221). That is one successful test, not long-term proof.
Timeline from September 15 to October 22, 2026: the Homebrew release archive stays at v0.6.0 while the installer's main branch gets PR #1004 on October 12, so Homebrew users use the workaround or disable the router and installer users run the update command

Installed with the one-line installer: update the checkout to main

The recommended installer clones the router to ~/.local/share/codex-router from main, so you can pull the fix now (updates and rollback):

sh
cd ~/.local/share/codex-router
./bin/model-router codex update
./bin/model-router codex doctor --fix
  • update needs a main checkout with no edits to tracked files. Untracked files do not block it. --force discards tracked edits but keeps untracked ones.
  • The previous revision is kept, and ./bin/model-router codex rollback returns to it. Re-running the installer also updates from main.
  • doctor --fix brings the generated config and the background service in line with the new code.

Then fully quit Codex (not only the window) and reopen it. Start a turn and send a follow-up while it is working. If the old message still appears, the router service is still running the old code: run ./bin/model-router codex stop, then ./bin/model-router codex start, and repeat the grep -c check above.

Installed with Homebrew: brew upgrade still gives you v0.6.0

As of October 22, 2026, the latest tagged release is v0.6.0, published September 15, 2026, before the fix (releases). The Homebrew formula downloads the v0.6.0 archive and has no option to build from main. brew upgrade codex-router therefore cannot deliver PR #1004 yet.

Until a release newer than v0.6.0 appears, pick one:

  • Use the instant_interrupt workaround below and keep the router.
  • Disable the router's Codex integration, then turn it back on after the next release.
  • Switch to the installer-based setup, which tracks main. Remove the Homebrew install first with codex-router uninstall and brew uninstall codex-router, so two copies do not manage the same Codex config.

When a newer release ships, run brew upgrade codex-router, then fully restart Codex and test a follow-up mid-turn.

Temporary workaround: set instant_interrupt = false

If you cannot update the router yet, you can turn off the Codex behavior that sends the rejected message. Add this to ~/.codex/config.toml, or add only the second line if a [features] table already exists (where config.toml lives):

toml
[features]
instant_interrupt = false

Fully restart Codex and check that codex features list now shows instant_interrupt off.

Treat this as a reversible stopgap, not a confirmed fix:

  • The expected effect comes from reading Codex's source: with the flag off, Codex no longer cuts a running response short when you steer, so no response.interrupt message is sent. No public report confirms it yet. The person who traced the bug in #52221 noted they had not tested it.
  • The cost is slower steering. A follow-up or question-card reply waits until the current step finishes instead of interrupting it.
  • instant_interrupt is an under-development flag, so a later Codex release can change or remove it.

Remove the line once your router includes PR #1004.

Bypass the router: disable or uninstall codex-router

If you added the router for a model you no longer need, take it out of Codex's path. One reporter in #52221 confirmed that the error stopped after they removed codex-router.

GoalInstaller-based setupHomebrew
Turn it off, keep everything for later./bin/model-router codex disable (undo with enable)codex-router disable
Remove it./bin/model-router codex uninstallcodex-router uninstall, then brew uninstall codex-router

disable removes only the Codex integration. uninstall removes the service and the managed Codex config but keeps the checkout, logs, backups, and stored provider credentials (common commands). You can delete the managed block from config.toml by hand, but the supported commands also handle the service.

Fully restart Codex afterward. Native GPT models then connect directly. Every external model you reached through the router disappears from the model picker until you turn it back on.

Will my Codex follow-up be lost after the 400?

Nobody has confirmed either way, so save it yourself. Reports differ:

  • The turn usually ends at the error.
  • In one Codex Remote case on an iPhone, the turn still showed as working, with a Continue button.
  • In one Windows case, the question-card reply was acknowledged, but the assistant's answer could not be read while the executed-command cards stayed visible.

Before you retry, copy the text of your follow-up or question-card answer. After you apply one of the fixes above, continue the task and resend it.

Seeing it with no proxy in between? Report it on openai/codex #52221

As of October 22, 2026, no public report shows this exact string on a direct connection between Codex and OpenAI, and OpenAI has not replied on #52221. If your config check found no local proxy or custom provider, your case is outside the router explanation and worth reporting there with:

  • Codex version (Desktop build number, or codex --version for the CLI) and platform
  • When it happened, with time zone
  • What you did right before: follow-up, question-card reply, or interrupt
  • Whether openai_base_url or a custom model provider is set (yes or no, not the URL)
  • The instant_interrupt line from codex features list

Leave out API keys, tokens, the router's capability URL, and private chat text.

Two similar-looking problems have different causes. If Codex shows Reconnecting in a loop instead of a 400, see Codex Keeps Reconnecting? Find the Cause Without Losing Your Task. One Windows user first saw this error right after a failed context compaction. If compaction itself fails, see Codex Remote Compact Failed: Read the Suffix Before You Retry.

For gateway and proxy authors: accept response.interrupt and response.steer

A Responses gateway that whitelists only response.create breaks Codex steering, whichever model sits behind it.

  • Codex's interrupt. Codex sends {"type":"response.interrupt","response_id":"…","mode":"discard_partial_items"} while a response is in progress and expects response.interrupt.accepted or response.interrupt.failed back. PR #1004 is a working reference: handle it outside the generation queue, abort the upstream request, finish with response.incomplete (reason interrupted), keep the socket usable, and ignore late interrupts that arrive after completion.
  • The public steering event. OpenAI's mid-turn steering guide documents a different client event, response.steer (with previous_response_id and input), for GPT-6 family models over WebSocket only. The server answers with response.steer.accepted, .pending, or .failed, and the steered response ends as response.incomplete with reason steered. response.interrupt appears in Codex's source but not in the public guides. One user's guess that Codex sends response.steer here (codex-on-crack #11) does not match the current client code. Accepting both events covers both clients.
  • No WebSocket at all. A custom provider entry with supports_websockets = false keeps Codex on HTTPS Responses. Coder's AI Gateway docs use this workaround. It does not apply when you redirect the built-in OpenAI provider with openai_base_url, because that provider has WebSocket support turned on. Whether steering behaves the same over HTTPS is not documented.

To set up and test a custom provider, see Connect Codex to a Third-Party Model API Without Guessing the Protocol.

Codex response.create error: quick answers

Is “Responses WebSocket messages must have type response.create” an OpenAI outage? No report links it to an OpenAI outage. In every traced case, the string comes from a local router's WebSocket check, so OpenAI's status page will not explain it.

Does brew upgrade codex-router fix it? Not as of October 22, 2026. The formula still installs v0.6.0, which predates PR #1004. Update an installer-based checkout instead, or use the workaround until a newer release ships.

Does it affect Codex Remote on iPhone? Yes, when the Mac that runs the turn routes Codex through the router. Apply the fix on the Mac. Nothing changes on the phone.

Can I keep codex-router and still steer mid-turn? Yes, once the running router includes PR #1004. Confirm with the grep -c check, then restart the router service and Codex.

Found an error, or hit a problem this article does not cover? Send the article URL and what you saw to hi@laozhang.ai. We check it and update the article.