Repository navigation
[Bug] Claude Code DeepSeek route fails on built-in Artifact tool schema #5658
Description
Client or integration
Claude Code
Provider or upstream service
deepseek (route: ocx-deepseek-flash → requested model deepseek/deepseek-flash)
OpenCodex version
2.60.0
Environment
- OS: Debian GNU/Linux 12 (bookworm)
- Architecture: x86_64
- OpenCodex: 2.60.0
- Claude Code: 2.1.263
- Node: v22.22.2
- OpenCodex multi-agent mode: v1
Endpoint or capability
Tool calls / function-calling schema — specifically the JSON Schema for Claude Code's built-in Artifact tool, sent as part of the tool-definition set on every request through this route.
Current behaviour
Every tested request through the ocx-deepseek-flash route failed with an HTTP 400 before any model output is produced. The upstream provider rejects the JSON Schema for Claude Code's built-in Artifact tool. This reproduces on a plain bounded request (no attempt to invoke the tool — the rejection happens during request/schema validation, before any tool call is made).
Expected behaviour
The route should complete successfully like the other tested OpenCodex Claude Code routes. In the same session, under the identical launch mode (ocx-code, Claude.ai MCP disabled, strict MCP configuration), the following routes all returned HTTP 200 and the exact requested output: ocx-qwen3-8-27b, ocx-minimax-m3, ocx-gpt-5-6-sol, ocx-gpt-6-astra. This is a concrete client-observed requirement (working sibling routes in the same harness), not a specific published DeepSeek schema specification (see "Upstream documentation" below).
Minimal redacted request or reproduction
-
Launch Claude Code in
ocx-codemode with Claude.ai MCP disabled, strict MCP configuration. The localocx-codeshortcut expands to:ENABLE_CLAUDEAI_MCP_SERVERS=false \ ocx claude --model sonnet --strict-mcp-config
-
Invoke the
ocx-deepseek-flashsubagent with any bounded task (tested: a no-tool, no-file task requesting a fixed sentinel string in response). -
Observe the call fail before any response is produced.
Reproduced in two separate runs:
- Original verification run — run ID
ocx-verify-20260922T150542Z-be2d9d6c. - Isolation test with
Artifactexplicitly added to the subagent'sdisallowedToolsfront-matter field — run IDdeepseek-noartifact-20260922T152244Z-28920a04.
No request body was captured or is available to redact/attach beyond the ocx logs --json metadata below; the client (Claude Code) constructs the tool schema set internally.
Actual response or error
Invalid schema for function 'Artifact': {"pattern":"^[^\u0000]*$","maxLength":1024,"minLength":1,"type":"string"} is not valid under any of the schemas listed in the 'anyOf' keyword
HTTP status 400. Observed identically, verbatim, in both runs. Model sent to the API: claude-ocx-deepseek--deepseek-flash.
Correlated ocx logs --json metadata (non-secret fields only):
| Test | requestedModel | provider | resolvedModel | status | durationMs |
|---|---|---|---|---|---|
| Original run | deepseek/deepseek-flash | deepseek | null | 400 | 593 |
Isolation run (disallowedTools: Artifact) |
deepseek/deepseek-flash | deepseek | null | 400 | 641 |
| Other 4 routes, same session (for contrast) | qwen3.8-27b / MiniMax-M3 / gpt-5.6-sol / gpt-6-astra | qwen-local / minimax / openai / openai | (each matched) | 200 | 1.5s–25.9s |
Upstream documentation
No public DeepSeek specification describing its function-calling JSON-Schema validator's accepted keyword set (e.g. its handling of anyOf) was located or consulted for this report; stating that clearly per the template's own allowance. Two independent third-party projects have documented closely related failures of the same Claude Code Artifact tool schema against other providers/proxies (see "Related reports" below) — those reports may be the closest available prior art, though they describe a pattern/regex rejection rather than the anyOf rejection observed here.
Suggested mapping or implementation notes
Any of the following would address this:
- Omit genuinely disallowed tools from the request sent upstream before request construction, rather than after (or in addition to) wherever filtering currently occurs — the isolation test showed
disallowedTools: ["Artifact"]at the agent-definition level does not currently prevent the tool's schema from reaching the DeepSeek request. - Sanitise or relax JSON Schema constructs in the
Artifacttool definition that this provider's function-calling validator does not accept (here, a string sub-schema nested underanyOf). - Provide a provider capability switch/flag that suppresses unsupported built-in tools (such as
Artifact) for routes known not to support their schema.
Precedent for the schema-sanitisation approach exists in unrelated projects that hit the same Claude Code Artifact tool schema against other backends (see below) — one of them (CLIProxyAPI) shipped a fix by stripping/sanitising unsupported regex constructs before forwarding the schema.
Related reports (corroborating evidence, not OpenCodex duplicates)
These are different projects, different proxies, and — as far as could be determined — a different specific schema keyword than the one observed here, so they are not duplicates of this issue. They are included because they independently confirm that Claude Code's built-in Artifact tool schema is a recurring source of incompatibility with third-party providers'/proxies' function-calling JSON-Schema validators:
QuantumNous/new-apiissue #7303 — reports DeepSeek's gateway rejecting apatternregex (using Unicode property classes and a negative lookahead) inside Claude Code'sArtifacttool schema, with the errorInvalid schema for function 'T': "..." is not a "regex". Closed as "not planned" (filed as an opt-in feature request, not accepted as scoped).router-for-me/CLIProxyAPIissue feat(gui): autosave featured subagent roster edits #5644 — reports the sameArtifacttool'spatternfield (Unicode property classes + lookahead) being rejected by strict JSON Schema validators when translating Claude→OpenAI-compatible requests, with error textInvalid schema for function 'Artifact': ... is not a 'regex'. Fixed via PR devlog: close the 2.64 release round #5677 (stripping/sanitising the unsupported regex constructs before translation).
Note the distinction: those two reports describe a pattern-is-not-a-valid-regex rejection on a different Artifact field (using \p{Cc}-style Unicode property classes and a negative lookahead). The failure recorded in this draft is an anyOf schema-composition rejection on a simpler pattern (^[^\u0000]*$). Both are plausibly part of the same broader class of problem — Claude Code's Artifact tool schema using JSON Schema constructs that not every provider's function-calling validator accepts — but this report does not claim they share an identical root cause.
Additional context: attempted mitigation
Added disallowedTools: ["Artifact"] to the ocx-deepseek-flash subagent's front matter as a temporary, controlled test (file backed up before the change and restored byte-for-byte afterward; original and restored SHA-256 matched exactly). The mitigation did not change the outcome — the identical schema rejection occurred, with the same status (400), same null resolvedModel, and the same error text. This narrows the finding (agent-level tool exclusion alone does not fix it) without establishing which internal component is responsible.
Reported context, not independently verified in this diagnostic session: deepseek-flash has reportedly completed successfully via the Codex-native subagent route, which — if accurate — would suggest the fault is specific to the Claude Code request/tool-schema construction path rather than to the DeepSeek model or provider generally. This report does not claim which internal component is definitely responsible.
Duplicate search
Searched lidge-jun/opencodex issues (read-only, no issue created/commented) for: Artifact, DeepSeek, anyOf, Invalid schema for function, and Claude Code DeepSeek schema-style terms. No exact or near-duplicate issue was found — no open or closed issue in that tracker mentions the Artifact tool, an anyOf schema rejection, or this exact error text.
One issue in the same tracker is topically adjacent but not a duplicate: #5421, "DeepSeek thinking mode HTTP 400 (reasoning_text must be passed back) when a routed subagent thinking turn lost its reasoning text" — same provider (deepseek) and same HTTP status (400), but a distinct root cause (a reasoning-text passback requirement, not a tool-schema/anyOf rejection).
This search was not exhaustive (a fixed set of targeted queries against the tracker's search UI); it should not be treated as a certainty that no related issue exists.
Security statement
No API keys, OAuth tokens, credentials, or other secret/environment material are included anywhere in this report. The metadata fields actually displayed above (requestedModel, provider, resolvedModel, status, durationMs) are non-secret request metadata from ocx logs --json, and the error text is a schema-validation message containing no credential data.
Checks (per template)
- Searched existing provider and compatibility issues (see "Duplicate search" above; not claimed exhaustive).
- The request and response were redacted (no request bodies were available to redact beyond the sanitised error text and log metadata above; nothing further was withheld).
- The expected behaviour is based on a concrete client requirement (four sibling routes succeeding in the same session, same launch mode) rather than a published upstream specification, since no such specification was located (see "Upstream documentation").
Activity
lidge-jun commentedon Sep 23, 2026
Looked at this during the 260923 bundle round (lane C, #5678) and could not pin the cause without a failing request. DeepSeek documents
minLength/maxLengthas unsupported only in strict mode, and the Claude inbound path sendsstrict: false. The failing field'spatternis a plain regex (no\p{...}class or lookahead), so it is not the regex rejection OpenCodex already strips. Could you share the full 400 response body from DeepSeek and your Claude Code version? With that, the offending keyword can be stripped for this route specifically.ChrisSpotta commentedon Sep 23, 2026
Thanks. I’ve performed a bounded retest with:
The original failing runs used OpenCodex 2.60.0 and Claude Code 2.1.263.
DeepSeek is no longer in my five-subagent roster, so I sent one direct loopback request through OpenCodex’s
/v1/messagesendpoint toclaude-ocx-deepseek--deepseek-flash.The request contained the isolated schema previously rejected:
{ "anyOf": [ { "pattern": "^[^\\u0000]*$", "maxLength": 1024, "minLength": 1, "type": "string" }, { "type": "null" } ] }The current result was HTTP 200, resolved model
deepseek-flash, with no schema error.OpenCodex request ID:
This was a minimal direct Messages request with
tool_choice: none, not a byte-for-byte replay of Claude Code’s complete built-inArtifactschema or the original generated-subagent path.I can therefore confirm that the isolated schema no longer reproduces the failure, but I cannot yet confirm that the complete Claude Code route is fixed. Since the retest returned HTTP 200, there is no current 400 response body to provide.
If you need the complete Claude Code path retested, please let me know the preferred supported capture method and I’ll run one bounded test.
lidge-jun commentedon Sep 26, 2026
Fixed on
devby #5892, carried in #5901 (merge dd1e327). It ships with the next release.fix(providers): sanitize Claude Code Artifact tool schema for DeepSeek (