Codex sub-agents return nothing on an external provider: agent_message and the empty "Payload:"
With Multi-Agent V2, Codex passes work between agents as agent_message items. OpenAI's backend understands them. Every other Responses implementation either refuses the request or silently drops the item, and a sub-agent that never receives its task cannot do it.
Last verified September 17, 2026 against openai/codex issue #33551, openai/codex issue #26234 · 5 min read
| Tool | OpenAI Codex CLI and Codex Desktop with Multi-Agent V2, where the sub-agent's model runs on a provider other than OpenAI |
| Symptom | The sub-agent fails at once with a 422 or "did not match any expected variant", or it runs and reports that it was given no task |
| Cause | The task is delivered as an input item of type agent_message, which is not part of the Responses API |
| When it happens | On every hand-off to a sub-agent, and again when the sub-agent's answer is replayed in the parent's history |
| Can you wait it out? | No |
- If the parent model is not an OpenAI model, the task text inside the item is plain text, and a proxy or the provider can turn the item into a normal user message. That fixes it.
- If the parent is an OpenAI model, the task text is encrypted with a key only OpenAI holds. No third party can read it. Keep that task tree on Multi-Agent V1, or run the sub-agent on OpenAI too.
What Codex sends
{
"type": "agent_message",
"author": "/root",
"recipient": "/root/worker",
"content": [
{ "type": "input_text", "text": "Message Type: NEW_TASK\nTask name: /root/worker\nSender: /root\nPayload:\n" },
{ "type": "encrypted_content", "encrypted_content": "<the task text>" }
]
}We captured this from codex-cli 0.154.0 with --enable multi_agent_v2. Despite its name, the encrypted_content block held the task as plain text, because the parent model was not an OpenAI model. When the parent is an OpenAI model, the block holds ciphertext that starts with gAAAAA.
unexpected status 422 Unprocessable Entity: {"error":"Failed to deserialize the JSON body into the target type:
data did not match any variant of untagged enum ModelInput"}
invalid request body: Invalid 'input': value did not match any expected variant
(no error at all) the sub-agent starts, sees only "Payload:" with nothing after it, and answers that it has no taskThe two cases
| Parent model | Sub-agent's provider | Task text | Fixable outside Codex? |
|---|---|---|---|
| Open model on an external provider | Same or another external provider | Plain text | Yes: convert the item to a user message |
| OpenAI model | External provider | Encrypted by OpenAI | No. Only a change in Codex can fix this |
| Any | OpenAI | Either | Nothing to fix |
A separate Codex bug, #40858, makes sub-agent roles ignore their own model_provider setting. If your sub-agent's requests go to the parent's provider, that is the bug you are hitting, and no provider can fix it.
Fix 1: keep the task tree on Multi-Agent V1
V1 delivers tasks as ordinary messages. The version is fixed by the first turn of a task and inherited by every agent it spawns, so start the task with a model for which Codex selects V1 and leave multi_agent_v2 off. V1's sub-agent tools are still sent inside a namespace, so on an external provider you also need one of the fixes on the MCP tools page.
Fix 2: a local proxy or a patched Codex
Commenters on the issue published a Node proxy that rewrites plain-text agent_message items into user messages and leaves ciphertext alone, and two patched builds that deliver the task as a user message in the first place. All three are linked from the issue.
Fix 3: a provider that converts the item
Our Responses endpoint turns every agent_message item into a user message made of the header and the task text, in the sub-agent's requests and in the parent's replayed history. If the task text is OpenAI ciphertext, the request still succeeds: the model is told the task could not be read, and the response carries an X-Agent-Message header so you can see why the sub-agent came back empty-handed.
codex exec --enable multi_agent_v2 "Spawn a sub-agent to write the unit tests while you write the module."| Model | MCP tools | Sub-agents | Sub-agents, Multi-Agent V2 | Note |
|---|---|---|---|---|
deepseek-ai/DeepSeek-V4-Flash | Pass | Pass | Pass | |
deepseek-ai/DeepSeek-V4.1-Flash | Pass | Pass | Pass | |
deepseek-ai/DeepSeek-V4-Pro | Pass | Pass | Pass | |
zai-org/GLM-5.3 | Pass | Pass | Pass | |
zai-org/GLM-5.3-Flash | Pass | Pass | Pass | |
MiniMaxAI/MiniMax-M3 | Pass | Pass | Pass | |
moonshotai/Kimi-K2.7-Code | Pass | Pass | Pass | In one V1 run the parent delegated and also computed the answer itself. |
openai/gpt-oss-120b | Pass | Pass | Unreliable | V2: passed 1 of 3 runs. GPT-OSS writes tool names in its own style (collaboration.spawn_agent) and sometimes calls sub-agent tools from inside the sub-agent, where they are not offered; the endpoint normalises the names, the second problem is the model. |
Qwen/Qwen3-VL-235B-A22B-Instruct | Pass | Pass | Pass | |
meta-llama/Llama-3.3-70B-Instruct-Turbo | Fail | Not run | Not run | Called the MCP tool, then announced the second step without doing it. Not suited to multi-step agent work. |
Run with codex-cli 0.154.0 on September 17, 2026. MCP test: a local MCP server holds order data the model cannot know; pass means the server logged both tool calls and the final answer contained the release code. Sub-agent test: the parent must delegate a SHA-256 of a random string; pass means the parent ran no shell command itself, a sub-agent was spawned, and the correct digest came back.
Frequently asked questions
Why does the sub-agent run but say it has no task?
The provider accepted the request and dropped the item it did not recognise. The sub-agent received the rest of the conversation, which ends in a header reading "Payload:" with nothing after it.
Can a provider decrypt a task sent by an OpenAI parent?
No. The key is OpenAI's. Anyone who says otherwise is guessing at the text.
I disabled multi_agent_v2 and still see agent_message items.
Users on the issue report that the model catalog can select V2 for some parent models regardless of the flag, and that the choice sticks to the task after its first turn. Start a new task with a parent model that resolves to V1.
Where Inference APIs fits
The conversion described in fix 3 runs on our Responses endpoint for every chat model, together with the namespace handling that Codex's sub-agent tools also depend on.
Something changed or wrong? Tell us and we will re-verify the entry.
