tool_search exposes deferred tools with an empty schema, permanently breaking threads on strict Responses providers (DeepSeek)
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
What happened?
On Codex Desktop with a custom provider that uses wire_api = "responses" (DeepSeek's official endpoint), a thread works normally until the model calls the built-in tool_search tool. From that point on every turn in that thread fails with:
Invalid schema for function 'codex_app::automation_update': schema must be a JSON Schema of 'type: "object"', got 'type: null'.
This looks related to the automation_update schema reports (#37786, #39848, #36441) but the trigger is different: in this build automation_update is deferred (deferLoading: true), it is not part of the initial tool manifest, and the request only starts failing after tool_search returns it. The failure then sticks for the rest of the thread.
Environment
- Codex Desktop app
26.818.61809, bundled CLI0.149.0-alpha.4.3 - macOS 15.6.1 (arm64)
model_provider = "deepseek",base_url = "https://api.deepseek.com/",wire_api = "responses",model = "deepseek-v4-flash"with the custommodels.jsoncatalog from the DeepSeek integration guide- Browser /
node_replMCP server enabled
Steps to reproduce
- Use a custom Responses provider (DeepSeek) in the Desktop app.
- Let the agent call
tool_searchwith a query that matches a deferred tool — e.g."automation"(matchescodex_app::automation_update), or a query that matches deferred MCP tools such asmcp__node_repl::js. - The very next sampling request fails with the 400 above. All following turns in the same thread fail the same way.
Observed locally: thread A failed at 10:24:08, 10:25:06, 10:25:24, 10:26:52; a second thread failed at 10:27:31 and 10:28:16 — same error every time, always right after a tool_search call.
Evidence
The tool_search result item reports the matched tools with inputSchema: null, for both app tools and MCP tools:
[
{"type":"namespace","name":"mcp__node_repl","tools":[
{"name":"js","inputSchema":null},{"name":"js_reset","inputSchema":null}]},
{"type":"namespace","name":"codex_app","tools":[
{"name":"automation_update","inputSchema":null},{"name":"open_in_codex","inputSchema":null}]}
]
Minimal payloads sent straight to https://api.deepseek.com/responses:
| tool payload | result |
|---|---|
namespace tool, "parameters": {} |
the exact error above |
namespace tool, "parameters": null |
accepted |
| namespace tool, normal object schema | accepted |
flat function named codex_app::automation_update |
rejected earlier, for the name pattern — so the flat form is not what is being sent |
So the follow-up request appears to carry parameters: {} for tools that were discovered through tool_search, and DeepSeek's schema validation rejects the whole request because of it. Note that automation_update is only the first reported tool because the namespace is validated in order; the same request also contains schema-less MCP tools.
Expected behavior
Tools surfaced by tool_search should carry their real JSON schema, or at minimum a valid placeholder such as {"type": "object", "properties": {}}, so that strict OpenAI-compatible providers accept the request.
Impact
- One
tool_searchcall permanently bricks the thread on strict providers: every later turn fails before the model can answer. - Not recoverable from the prompt side; the user has to abandon the thread.
- Affects any custom Responses provider that validates tool schemas (DeepSeek, and presumably other strict OpenAI-compatible endpoints).
Workaround
A local proxy in front of the provider that rewrites an empty parameters to {"type": "object", "properties": {}, "additionalProperties": true} makes the identical request succeed (validated end-to-end, including streaming), which confirms the empty schema is the only problem. That is a client-side workaround, not a fix.
Related
- #37786 —
automation_updaterootoneOf/type: nullon DeepSeek (eager-loaded path) - #39848, #36441 — same error class on other strict providers
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing the tool_search result construction into the follow-up Responses request, focusing on how deferred app and MCP tools are represented. Reproduce the DeepSeek request with a deferred tool, then verify that discovered tools carry their real schema or a valid object schema and that subsequent turns no longer fail.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api, backend-api-design
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 66/100