Repository navigation
Subagent task payload invisible to custom non-OpenAI providers (DeepSeek): encrypted_content block dropped with multi_agent_version v2 #36586
Description
Summary
When using a custom non-OpenAI provider (DeepSeek) with Codex multi-agent (subagents), tasks
dispatched via spawn_agent / followup_task are never seen by the subagent. The subagent
initializes, reads the workspace, and then replies that it has no task input ("no new task
input"), so every subagent run is a no-op.
Environment
- Codex CLI
0.145.0on Windows - Custom provider in
config.toml:[model_providers.deepseek]with
base_url = "https://api.deepseek.com/"andwire_api = "responses" model = "deepseek-v4-flash",model_catalog_json = "~/.codex/models.json"(written by the
official DeepSeek one-command setup, which sets"multi_agent_version": "v2"in both entries)- Multi-agent tools:
collaboration__spawn_agent/followup_task(MultiAgentV2 tool surface)
Reproduction
- Start a session with
deepseek-v4-flash(models.json entry has
"multi_agent_version": "v2"). - Spawn a subagent with a simple task, e.g. "reply exactly: PONG".
- The subagent replies with a workspace status summary and asks what to work on; it never
executes the dispatched task.followup_taskwith the same prompt also yields
"no new task input".
Same setup with a native catalog model (e.g. gpt-5.6-sol) executes subagent tasks correctly.
Root cause
For MultiAgentV2, the task payload is always delivered through an
InterAgentCommunication::new_encrypted(..) record whose content is a
[input_text header, encrypted_content] pair:
codex-rs/core/src/tools/handlers/multi_agents_v2.rs—communication_from_tool_message
usesnew_encrypted(author, recipient, vec![], message, trigger_turn)for every
non-DirectPlaintextMessagesource, so the task text lives in theencrypted_content
content block.codex-rs/core/src/client.rs— inbuild_responses_request, the!is_openaibranch only
clearsFunctionCall.encrypted_function_args; it does not handle
ResponseItem::AgentMessagecontent, so theencrypted_contentblock is sent to the
provider API unchanged.codex-rs/core/src/client_common.rs—get_formatted_input_for_requestpasses the items
through as-is.
For OpenAI endpoints the agent-message payload is decrypted/rendered server-side, which is why
native models work. The DeepSeek Responses API does not understand the encrypted_content
content block type and drops it, so the model only receives
Message Type: NEW_TASK\nTask name: ...\nSender: ...\nPayload: with an empty payload.
Workaround (validated)
Set "multi_agent_version": "v1" for the custom model entries in ~/.codex/models.json
(instead of "v2"). With V1 the spawn initial input is delivered as a plain user message
(SpawnInitialInput::UserInput) instead of an inter-agent communication, and DeepSeek
subagents execute tasks correctly (verified with a codex exec spawn test returning the
expected output). Restart the session after editing models.json.
Note: this means custom non-OpenAI models cannot currently use the MultiAgentV2 tool surface
at all.
Suggested fix
In build_responses_request (codex-rs/core/src/client.rs), extend the !is_openai branch
to transform ResponseItem::AgentMessage content: fold the encrypted_content block into a
single InputText (header text + payload), mirroring the plaintext rendering already used by
communication_from_tool_message's DirectPlaintextMessage path. This lets custom
wire_api = "responses" providers receive subagent task payloads while keeping V2 behavior.
Related
- MCP tools silently unavailable with DeepSeek official setup (custom models.json: supports_search_tool=true + tool_mode=null) #36382 — MCP tools silently unavailable with DeepSeek official setup (same family:
custommodels.json+ non-OpenAI provider gaps in tool/content handling)
Activity
github-actions commentedon Aug 2, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
0.147.0-alpha.4: OpenAI parent still sends encrypted V2 task to non-OpenAI child after #35845 #36376Powered by Codex Action
CCanxue commentedon Aug 3, 2026
Additional reproduction and a working local patch (codex-cli 0.146.0 source, deepseek-v4-flash,
wire_api = "responses",multi_agent_version = "v2").Wire-level finding: even when the
agent_messagecontent is rewritten so the payload is plaintextinput_text(noencrypted_content), DeepSeek still ignores theagent_messageitem in most runs and the child replies that it has no task. The task is only consumed reliably when it is delivered as a plainusermessage.Working local patch: for non-OpenAI providers, deliver V2 spawn / followup_task / send_message payloads as plain user input (
UserInput), mirroring the V1SpawnInitialInput::UserInputpath. The OpenAI path keeps encryptedInterAgentCommunicationunchanged. Verified end-to-end with the real provider:spawn_agent->PONG,followup_task->PONG2.The
multi_agent_version: "v1"workaround does not work with deepseek-v4-flash: with DeepSeek's catalog (supports_search_tool: true), the V1 tool family is registered as Deferred behindtool_search. Thetool_searchtool is present in the request tools array, but deepseek-v4-flash does not recognize or call it (same family as #36382), so the V1 tools are unreachable and v1 stays broken for this model.Full patch, build/install steps, QA evidence and config templates (sanitized) are published here: https://github.com/CCanxue/codex-deepseek-subagent-fix
liushiyao110 commentedon Aug 4, 2026
Reproducing on Codex Desktop (macOS), app version 26.727.51351, with
deepseek-v4-flashvia the official one-command DeepSeek setup (wire_api = "responses",multi_agent_version: "v2"in models.json).Both
spawn_agent(initial task) andfollowup_taskfail the same way: the child initializes, reads the workspace, then replies "I don't see a task in this message yet" / "Ready when you are — just send over the task."Log evidence (rollout JSONL): the delivered
agent_messagecontains aninput_textenvelope with an emptyPayload:plus anencrypted_contentblock holding the actual task. The HTTP request POSTed tohttps://api.deepseek.com/responsescarries thatencrypted_contentblock untouched, so DeepSeek only sees the empty envelope. Normal user messages are plaininput_textand work fine in the same session.This confirms the issue is not CLI-only — it also affects the Desktop app with a non-OpenAI model as the root agent. Happy to provide full rollout logs or test a proposed fix.
trillox9 commentedon Aug 4, 2026
Same issue on codex-cli 0.146.0 with the official DeepSeek setup
Environment:
wire_api = "responses", base_url = "https://api.deepseek.com/",
model_catalog_json with multi_agent_version = "v2"
Reproduction:
(e.g. "run Get-Date and return the output").
with a generic greeting: "I'm ready to help. What would you like me to
work on?"
receives/executes the follow-up.
replays the parent's spawn calls from the inherited context, creating
recursive spawn chains (I had to interrupt them manually).
What I found in the logs:
input item of type "agent_message", with the real payload inside
"encrypted_content". The visible content stops at "Payload:" with an
empty text.
item types: message, function_call, function_call_output, reasoning,
web_search_call. It explicitly says other types are ignored.
Expected behavior:
user message (like the community patch does), or Codex should fail fast
with a clear "provider does not support agent_message input items"
error instead of silently dropping the task.
Happy to provide redacted request/response logs or test a patched build.
CCanxue commentedon Aug 6, 2026
Thanks for the additional reproductions. trillox9's point about DeepSeek's Responses compatibility table (only
message,function_call,function_call_output,reasoning, andweb_search_callare accepted; other input item types are ignored) explains why foldingencrypted_contentinto plaintextinput_textinside theagent_messageitem is not enough: the whole item type is dropped, not just the encrypted block.For anyone who wants to test a fix now: we published a local patch for 0.146.0 that delivers V2 spawn / followup_task / send_message tasks as plain user messages for non-OpenAI providers (OpenAI path unchanged), verified on CLI and applicable to the Desktop bundled CLI. liushiyao110, the build/install steps in the repo cover the Desktop case as well. We also saw the same recursive spawn chains with
fork_turns: allthat trillox9 described, when the parent prompt leaks into the child context.Repo: https://github.com/CCanxue/codex-deepseek-subagent-fix
docs(icon): Icon 文档、差距分析与审查流程更新
TOV1C commentedon Aug 11, 2026
Root cause
MultiAgentV2 delivers tasks through InterAgentCommunication: the plaintext content is empty, and the entire task text is placed in the encrypted_content content block. OpenAI endpoints decrypt and render this block server-side, which is why native models work; DeepSeek's Responses implementation does not recognize encrypted_content (community packet captures show that sending a request containing this content item directly returns a 400 / it gets dropped), so the sub-agent only sees an empty Payload:. This is the same family of issues as openai/codex #36493, #36321, #36586, #37237, and #37822.
Verified workaround (currently in effect on this machine)
Modify both entries in ~/.codex/models.json:
Rationale: supports_search_tool=false makes search_tool_enabled=false, so deferred tools are no longer hidden behind tool_search but are exposed directly to the model (the same mechanism as the workaround in MCP tools silently unavailable with DeepSeek official setup (custom models.json: supports_search_tool=true + tool_mode=null) #36382). The v1 tool family then becomes directly visible, and v1 delivers tasks as plain user messages (UserInput), which DeepSeek consumes reliably.
CCanxue commentedon Aug 14, 2026
Thanks for confirming the root cause and for the refined v1 workaround. We can confirm the mechanism: with
supports_search_tool = false, V1 tools are registered with direct exposure instead of being deferred behindtool_search, which addresses the discoverability problem we hit earlier with deepseek-v4-flash. This is a config-only change, so it also works on the official unpatched binary (no rebuild needed). The tradeoff is losing the V2 tool family (followup_task,send_message,list_agents,interrupt_agent); if you need those, a local patch that keeps V2 and delivers payloads as plain user messages for non-OpenAI providers is verified end-to-end: https://github.com/CCanxue/codex-deepseek-subagent-fixHaoYue1027 commentedon Aug 22, 2026
Additional reproduction: Codex Desktop (Windows) + DeepSeek official setup, CLI 0.149.0 / app 26.818.5229.0 — confirms the same
encrypted_contentdrop; workaround (multi_agent_version v1) matches your analysis.Environment
26.818.5229.0; embedded runtimecodex-cli 0.149.0(internal kernel string0.149.0-alpha.4fromapp\resources\codex.exe; UI version string differs — checked the binary, not the UI, percodex-desktop-diagnostics).deepseek-v4-flash-vision-exp;model_provider = "deepseek";base_url = "https://api.deepseek.com/";wire_api = "responses".~/.codex/models.jsonwritten by the official DeepSeek one-command setup: all three entries (deepseek-v4-flash,deepseek-v4-pro,deepseek-v4-flash-vision-exp) have"multi_agent_version": "v2".Reproduction (stable)
spawn_agent(task_name="acceptance", message="...", fork_turns="none")andwait_agent.list_agentsshows the sub-agent completing with: “I only received session context; I don't see the concrete task content.”agent_messagein the session rollout (~/.codex/sessions/**/*.jsonl) is exactly:Payload:is empty. Reproduced 3+ times acrossspawn_agent/followup_task/send_message, both before and after a full Desktop restart.Causal isolation
models.json: setting"multi_agent_version": "v1"for the three DeepSeek entries delivers the task as plaintextuserinput and the sub-agent executes correctly (verified once, then rolled back).model_provider="deepseek"unchanged,fork_turns=1partially masks the issue (child receives forked history but still not the per-agent spawn message), whilefork_turns="none"yields a fully empty agent — consistent withencrypted_contentbeing dropped by the third-party endpoint.spawn_agentreturns success andfollowup_taskreturns silently empty.Boundary
v1is usable but loses the v2 tool surface (per your note) — I reverted it to keep the default v2 baseline while upstream decides.Request
This repo already tracks it in #37237/#37822; adding my Windows Desktop + 0.149.0 data point. Expected: task body reaches the sub-agent (plaintext expansion before serialization, or a loud error for third-party providers). Actual: empty
Payload:header, zero diagnostics. Please confirm and prioritize the suggested fix inbuild_responses_request(foldencrypted_contentintoInputTextfor non-OpenAI providers).maxronner commentedon Aug 29, 2026
Reproduced on codex-cli 0.147.0 with the deepseek provider (default profile). All three DeepSeek model entries in the local models.json catalog are multi_agent_version=v2, and every spawned sub-agent receives a NEW_TASK envelope with an empty Payload: (task text lands in encrypted_content, which the DeepSeek Responses API drops). Sub-agents run setup then report 'no task input'. Inline/sequential fallback works; the parallel sub-agent path is blocked. Setting multi_agent_version=v1 for the DeepSeek entries is the workaround I'm applying.
SuiltaPico commentedon Sep 14, 2026
Adds a Codex Desktop data point with one config variable I did not see called out in this thread:
disable_response_storage = true.Environment (sanitized)
26.908.408340.154.0-alpha.6.2deepseekhttps://api.deepseek.comresponsesdeepseek-v4-flashhigh, summaryconcisedisable_response_storagetruedanger-full-access/ elevated<REDACTED>(not included anywhere)Reproduction
spawn_agentwithfork_turns = "none"and a nonce-only task:{ "task_name": "probe", "fork_turns": "none", "message": "NONCE=4f7c2a; reply with only this NONCE." }followup_taskto the same child withNONCE=9d1b3eshows the same behavior.Child rollout shape:
The child model sees the header, but not the nonce.
Why I am adding this
This is the same failure family as this issue and #45233, but the Desktop build plus
disable_response_storage = trueis a slightly different configuration. I have not tested whether the bug persists withdisable_response_storage = false; I did not want to change that flag without a clean control.One request for maintainers: please confirm whether
disable_response_storageis part of the trigger, or whether it is incidental to the custom-provider path.Fix discussion
The later comments here (especially @CCanxue and @OneCuriousLearner) make sense: folding
encrypted_contentinto readable text insideagent_messagemay not be enough if the provider drops the wholeagent_messageitem type. What I can confirm from this Desktop run is only the observed behavior:spawn_agentreturns success.encrypted_content.If a provider-neutral plain user/message rewrite is the intended fix for non-OpenAI providers, it would help to also fail closed: a spawn should not report success if the child cannot see the payload.
Related but probably separate
While probing this, I also saw
fork_turns = "all"copy an in-flight parent turn. The child replayed the parent's spawn actions, created depth-2 agents, and exhausted the 4-agent slot limit. That looks like fork-semantics / recursion control rather than the same encrypted-content drop. I can file it separately if useful.Workaround status
I have not independently verified the
multi_agent_version: v1+supports_search_tool: falseworkaround on this exact Desktop build, so I am not claiming it works here. I am only adding the Desktop +disable_response_storagereproduction point.pluto1747-cybr commentedon Sep 25, 2026
Adding a 2026-09-25 data point on Codex Desktop
26.917.71314/ CLI0.155.0-alpha.16.4(macOS + Windows, DeepSeek Responses provider): themulti_agent_version: v1workaround works, but the effective value is selected at thread creation and cached for that thread.Changing the model catalog to v1 is not enough by itself — it needs a cold start (so the process reloads the catalog) and a new thread. Threads created before the change keep
turn_context -> multi_agent_version: v2across restarts; new threads read v1 and subagents inherit v1.features.multi_agent_v2 = falsedid not change the effective path in our tests.This may matter for the fix/UX: a catalog edit silently does nothing for existing threads.