MultiAgentV2 cross-provider subagent cannot consume encrypted task assignment
Summary
With an OpenAI parent and a correctly selected non-OpenAI custom-provider child, MultiAgentV2 delivers the child assignment only as encrypted content that the custom-provider model cannot consume.
The spawned child metadata shows the intended custom role, provider, model, and MultiAgentV2 implementation, but the child sees an empty visible Payload: and reports that it has no assignment.
A same-provider OpenAI child consumes an otherwise equivalent encrypted assignment successfully.
Environment
- Codex CLI: 0.145.0
- Surface: Codex desktop connected to a remote Codex app-server over SSH
- Multi-agent version: v2
- Parent provider: OpenAI
- Child role: registered custom agent
- Child provider: non-OpenAI, OpenAI-compatible Chat Completions provider
- Child model: Kimi K3
Reproduction
- Enable
multi_agent_v2. - Configure a registered custom child agent using a non-OpenAI provider and an OpenAI-compatible Chat Completions model.
- Start an OpenAI-backed parent.
- Spawn the custom child with a unique harmless marker and an explicit instruction to run
pwd. - Inspect the spawned session metadata and child handoff.
The session metadata confirms:
agent_roleis the registered custom role.multi_agent_versionisv2.model_provideris the intended custom provider.modelis the intended custom model.
The stored handoff displays an empty Payload: and contains an encrypted-content block. The child reports that it received no assignment and does not execute the marker or pwd.
In a related continuation path, the parent can terminate with:
Encrypted function output content could not be decrypted or decoded.
Control: same-provider child
Repeat the test with an OpenAI child.
Its stored handoff also displays an empty visible Payload: plus encrypted content, but the OpenAI child successfully consumes the assignment, reproduces the marker, runs pwd, and returns the expected path.
This indicates that general v2 dispatch works and that the problem is specific to the provider boundary.
Control: provider-neutral delivery
As a diagnostic only, provide the same assignment to the custom child through a single-use, permission-restricted, nonce-bound local file referenced by the custom agent's developer instructions.
The custom child then executes the marker and pwd successfully.
This confirms that:
- The custom provider and model are operational.
- Child provider/model selection works.
- The task itself is valid.
- Failure occurs when the assignment is carried only through the encrypted MultiAgentV2 field.
A fixed file inbox is not a safe permanent workaround because of stale-task, concurrency, cleanup, attribution, and local instruction-injection risks.
Expected behavior
Codex should make the assignment available to the selected child in a representation that its configured provider can consume.
Possible implementations include decrypting/translating the assignment within Codex before invoking a provider that does not support OpenAI encrypted tool parameters, or explicitly rejecting unsupported cross-provider delegation before spawning the child.
Actual behavior
The custom child is spawned with the correct role, provider, and model, but cannot consume the encrypted assignment and behaves as though no task was supplied.
Related issues
- #33267 - exact decryption error when consuming MultiAgentV2 child results
- #17541 - provider-specific encrypted content retained across a provider/model boundary
- #26753 - MultiAgentV2 agent tools use encrypted parameters
- #27205 - model not configured for encrypted tool use
- #17598 - native subagent orchestration with non-OpenAI custom providers
- #32753 - encrypted MultiAgentV2 assignments are no longer observable
Additional notes
This is not a request to support a CPA-backed parent. The failing topology is specifically:
OpenAI parent -> registered non-OpenAI custom-provider child
Please let me know which narrowly scoped logs or sanitized session records would help. I have intentionally omitted authorization data, internal network details, complete encrypted blobs, and unrelated prompts.
12 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Thanks — I reviewed #33551. It is closely related, but I do not think it is an exact duplicate.
#33551 focuses on external Responses providers receiving the OpenAI-specific
agent_messageitem type, which may be rejected even when the item is plaintext.This report reproduces with a custom provider using the OpenAI-compatible Chat Completions protocol. The child session is created with the correct custom role, provider, and model, but the assignment is available only through the encrypted MultiAgentV2 handoff and the child behaves as though no task was supplied. The same encrypted handoff is consumed successfully by an OpenAI child, while the same custom-provider child executes the task when it is delivered through a provider-neutral plaintext diagnostic.
So the reports may share the same broader need for provider-aware inter-agent transport, but they exercise different provider protocols and failure points. I am leaving this open for maintainer triage and am happy for it to be consolidated if the fix for #33551 is intended to cover Chat Completions providers as well.
You can change model catalogue to force v1. Anyways duplicate of #28058
Corroboration from Codex desktop multi-role session
Confirmed the same MultiAgentV2 cross-provider failure on Codex desktop / CLI 0.145.0-alpha.30, macOS
Darwin 25.5.0 arm64, with project-local agents under.codex/agents/.Topology
gpt-5.6-sol(MultiAgentV2)frontendrole →kimi-k3(custom/non-OpenAI)backendrole →gpt-5.6-sol(OpenAI control)skills_openapirole →grok-4.5via external Responses gateway (separate wire-format failure; see also #33551)Observed behavior (Kimi K3 child)
spawn_agent(agent_type=frontend, ...)succeeds; child thread starts with correct role/model.``
`Message Type: NEW_TASK
Task name: /root/frontend_...
Sender: /root
Payload:
encrypted_contentfollowed by
only (visiblePayload:` empty).followup_taskto the same child produces the same empty-payload handoff; child reasons "The parent agent hasn't sent a task yet" and stands by again.backendchild ongpt-5.6-solconsumes the assignment and executes immediately.developer_instructionsmakes the K3 child start working (reads repo). Same model channel is therefore operational; only the encrypted MultiAgentV2 assignment path fails.Follow-up
Parent eventually completed the work by temporarily spawning the same frontend role on
gpt-5.6-sol. Persistent project config remained on K3.Expected
If Codex allows selecting a non-OpenAI provider for a registered child role, MultiAgentV2 should deliver spawn/followup/send_message assignments in a representation that provider can consume (decrypt/translate at the boundary to a standard plaintext
messageitem), or fail fast with an explicit unsupported-cross-provider error before the child stands by forever.Related
agent_messageitems (matches our Grok path)Happy to provide redacted rollout excerpts (parent + child) if useful; avoiding full ciphertext and internal network details here.
Thanks — V1 can indeed be forced and works as a workaround, but it gives up quite a bit of V2 functionality.
My understanding is that #28058 is related but slightly different: it concerns the loss of a readable audit trail, whereas this issue concerns the encrypted handoff preventing a non-OpenAI sub-agent from receiving the task at all. The underlying mechanism may be the same, but the user-facing problems are different.
Additional data from codex-cli 0.146.0 source with a real DeepSeek child (wire-captured).
For the non-OpenAI -> non-OpenAI direction, the task text only ever reaches the child inside
encrypted_content; DeepSeek drops that block and the child sees an emptyPayload:. Even after rewriting theagent_messagecontent so the payload is plaintextinput_text, DeepSeek still ignores theagent_messageitem in most runs. The task is only consumed reliably when delivered as a plainusermessage.For the OpenAI -> non-OpenAI direction,
messageis real ciphertext withencrypted_function_args: null, so a local request transform cannot decrypt it (see also #36376).We published a local patch that delivers V2 spawn / followup_task / send_message payloads as plain user input for non-OpenAI providers (OpenAI path unchanged), verified end-to-end: https://github.com/CCanxue/codex-deepseek-subagent-fix
Follow-up after validating the plaintext collaboration changes shipped in
0.147.0-alpha.1via #35845:The cross-provider failure described here remains reproducible on a
0.147.0-alpha.4source baseline.The alpha.1 change added a valid plaintext receiving path, but that path is entered only when
encrypted_function_args == Some([])and the tool namespace is exactlycollaboration. The MultiAgentV2 schemas still unconditionally mark the message fields forspawn_agent,send_message, andfollowup_taskwith.with_encrypted(). An OpenAI parent therefore normally supplies actual encrypted arguments, the call remainsDirect, and the child assignment is still constructed withInterAgentCommunication::new_encrypted(...).This leaves the original third-party-provider topology unresolved. It also does not cover custom or absent tool namespaces, does not expose an explicit delivery policy, and does not fail before creating a child that cannot consume the encrypted task.
I implemented and tested a local source-level path with an opt-in plaintext delivery policy, a non-reserved tool namespace, end-to-end call classification, and a fail-closed guard. Targeted feature tests passed 34/34, targeted MultiAgentV2 tests passed 74/74, the release build succeeded, and a restarted Codex Desktop session successfully completed an OpenAI-parent to third-party-provider-child smoke test.
The full analysis, affected source paths, sanitized configuration shape, and validation results are documented in #37197.
Thanks for the detailed analysis. It matches what we observed on the non-OpenAI parent side: the #35845 plaintext marker is only entered with
encrypted_function_args == Some([]), which third-party parents do not emit, and even a plaintextagent_messageitem is dropped by DeepSeek (its Responses compatibility table only acceptsmessage,function_call,function_call_output,reasoning, andweb_search_callitem types).Our local patch takes a simpler route for the non-OpenAI parent case: V2 spawn / followup_task / send_message payloads are delivered as plain
UserInput(V1-style standard user messages), verified end-to-end with deepseek-v4-flash. It intentionally does not cover the OpenAI-parent -> third-party-child case, which still needs an opt-in plaintext delivery policy like the one you described; the fail-closed guard and end-to-end call classification sound like the right direction. Patch, wire evidence, and write-up: https://github.com/CCanxue/codex-deepseek-subagent-fix+1 we need to merge/fix this issue so codex is really usable with open-weighted models
CodexHub Beta4.1 follow-up (sanitized; no credentials or raw ciphertext included).
This issue remains an upstream producer/delivery limitation for the OpenAI-parent -> non-OpenAI-child direction. We reproduced that the child can be created correctly while the V2 assignment is available only through an
agent_message/encrypted_contenthandoff containing ciphertext. A Gateway downstream of the parent cannot decrypt that value; simply converting the item shape to a normal function/message or removing an Official schema flag does not restore the task body.CodexHub Beta4.1 has completed the separate Gateway adaptation work: V2 namespace declarations are presented to third-party models as ordinary function tools, aliases are deterministic and reversible, streamed/non-streamed calls and history are adapted, and Official OpenAI traffic remains passthrough. This is an adapter for ordinary tool-calling models, not a claim that they natively understand Codex V2.
The missing piece is provider-aware delivery on the upstream producer side: retain encrypted transport for OpenAI targets, but use a provider-neutral plaintext user/message path for non-OpenAI targets across spawn/follow-up/send-message, with a fail-closed error when unsupported. We are keeping the full external-V2 lifecycle gate open rather than marking it passed based only on tool declarations or a model catalog flag.
Reproduced on stable 0.147.0 with Vercel AI Gateway + DeepSeek
I reproduced the OpenAI-parent -> non-OpenAI-child failure on the current stable CLI, with a Responses-compatible hosted gateway rather than a local provider.
Environment
codex-cli 0.147.0gpt-5.6-sol/ MultiAgentV2deepseek_flash_0731wire_api = "responses"deepseek/deepseek-v4-flash-0731fork_turns = "none"Observed
model_provider = "vercel_ai_gateway"; the child turn used the intended DeepSeek model.spawn_agent.messagewas stored as encrypted content.This matches the empty/missing assignment behavior described here: provider/model selection succeeds, but the dynamic V2 handoff is not consumable across the provider boundary.
Controls
/v1/responsesendpoint with the exact model returned the expected canary.codex execsubprocess. DeepSeek then successfully created a minimal PythonHello, World!program, executed it, and verified exact stdout.That last control confirms that Vercel authentication, the DeepSeek model, Responses transport, tool execution, and file writes are all operational. The failure remains isolated to native MultiAgentV2 cross-provider task delivery.
This is a useful encrypted-handoff control/parser case for
codex-rescue. Rescue does not claim to decrypt or translate MultiAgentV2 provider-bound task payloads; I’m testing whether it can inspect the persisted parent/child sessions without crashing, inventing a plaintext assignment, or treating an unsupported cross-provider handoff as executed work.If you still have an affected local parent/child session pair, could you run:
A healthy/unknown/unsupported result is useful evidence here. Please do not salvage solely to test this provider transport bug; the important invariant is conservative diagnosis and no replay of unknown child work.
Sanitized output + versions/exit codes only—no ciphertext, raw rollouts/SQLite, prompts, provider credentials, or private paths. Repo: https://github.com/shleder/codex-rescue