Summary
When routing Zed IDE → 9router → Zed Hosted AI with Claude models (e.g. zd/claude-sonnet-5-5), the first turn succeeds, but the next turn that includes tool results fails:
[400]: failed to parse Anthropic request: missing field `is_error`
URL: https://cloud.zed.dev/completions
Repro
- Connect a Zed OAuth account in 9router.
- Point Zed IDE (or any OpenAI-format client) at 9router with a Claude model via the Zed provider.
- Ask something that triggers tools (e.g. list files).
- First completion succeeds; the follow-up with
role: tool messages returns 400.
Log pattern:
▶ POST zd/claude-sonnet-5-5 → zed/claude-sonnet-5-5 · FMT: openai→openai · … · 4 MSG · 12 TOOL
📊 DONE …
▶ POST … · 6 MSG · 12 TOOL
✗ ERROR 400 · … missing field `is_error`
Root cause
open-sse/executors/zed.js converts OpenAI Chat Completions → Anthropic for Claude models via openaiToClaudeRequest, then posts that as provider_request to cloud.zed.dev/completions.
Zed’s Anthropic wire types require is_error: bool on every tool_result (non-optional). Anthropic’s public API treats it as optional (default false), so our translator omitted it:
- OpenAI
role: "tool" → Claude tool_result never set is_error
- Claude-shaped passthrough only spread
is_error when truthy (...(part.is_error && { is_error }))
Fix
Always emit is_error (boolean) on every tool_result produced by openai-to-claude.js. Harmless for Anthropic’s public API; required for Zed’s strict serde parser.
Related
No existing issue/PR specifically for this Zed is_error 400. Closest known translator gap: OpenAI-bridge loss of tool_result.is_error documented in tests/translator/bugs-openai-bridge.test.js / tests/translator/AGENTS.md (Claude→OpenAI direction).
Summary
When routing Zed IDE → 9router → Zed Hosted AI with Claude models (e.g.
zd/claude-sonnet-5-5), the first turn succeeds, but the next turn that includes tool results fails:Repro
role: toolmessages returns 400.Log pattern:
Root cause
open-sse/executors/zed.jsconverts OpenAI Chat Completions → Anthropic for Claude models viaopenaiToClaudeRequest, then posts that asprovider_requesttocloud.zed.dev/completions.Zed’s Anthropic wire types require
is_error: boolon everytool_result(non-optional). Anthropic’s public API treats it as optional (default false), so our translator omitted it:role: "tool"→ Claudetool_resultnever setis_erroris_errorwhen truthy (...(part.is_error && { is_error }))Fix
Always emit
is_error(boolean) on everytool_resultproduced byopenai-to-claude.js. Harmless for Anthropic’s public API; required for Zed’s strict serde parser.Related
No existing issue/PR specifically for this Zed
is_error400. Closest known translator gap: OpenAI-bridge loss oftool_result.is_errordocumented intests/translator/bugs-openai-bridge.test.js/tests/translator/AGENTS.md(Claude→OpenAI direction).