fix(workers-ai-provider): flatten Anthropic system array on the run path by isaacrowntree · Pull Request #672 · cloudflare/ai · GitHub
Skip to content

fix(workers-ai-provider): flatten Anthropic system array on the run path - #672

Closed
isaacrowntree wants to merge 1 commit into
cloudflare:mainfrom
isaacrowntree:fix/anthropic-run-path-system
Closed

isaacrowntree wants to merge 1 commit into
cloudflare:mainfrom
isaacrowntree:fix/anthropic-run-path-system

Conversation

@isaacrowntree

Copy link
Copy Markdown

Problem

Every Anthropic request on the unified-billing run path (env.AI.run) fails as soon as it has a system prompt:

7003: Model execution failed (User Input Error): Invalid value at system: Invalid input: expected string, received array

@ai-sdk/anthropic always serializes system as an array of text blocks ([{ type: "text", text }]), which Anthropic's own API accepts. The run path validates system as a string only. Since runWireFormat: "anthropic" sends the AI SDK's native body through makeRunFetch verbatim, the request is rejected.

This breaks Think's string-model routing for every Claude model (getModel() { return "anthropic/…" }), because Think always sends a system prompt.

Repro (Worker with an AI binding, workers-ai-provider@4.0.0):

const provider = createWorkersAI({ binding: env.AI, providers: [openai, anthropic] });
await generateText({ model: provider("anthropic/claude-sonnet-5.5"), system: "Be terse.", prompt: "Say ok." });
// → 400, 7003 "Invalid value at system: … expected string, received array"

Fix

In makeRunFetch, when the run wire format is anthropic, join a text-only system array into a single string before calling binding.run. Bodies with non-text blocks are left alone. The gateway path (gw.run, provider-native endpoints) is unchanged, because it accepts arrays.

Block-level cache_control can't be expressed in a string, so it is dropped on this path. Those requests were rejected outright before, so this is strictly an improvement. If the run path later accepts arrays, this can be removed.

Testing

  • Unit tests: run path flattens (including a block with cache_control), gateway path leaves the array untouched, and helper edge cases (string, absent, non-text blocks). vitest: 22 files / 460 tests pass; tsc --noEmit clean.
  • Live, against the real gateway with the patched dist: anthropic/claude-sonnet-5.5 and anthropic/claude-sonnet-5, with a system prompt and a tool, now succeed via both generateText and streamText (all previously failed with 7003).

Changeset included (workers-ai-provider: patch).

🤖 Generated with Claude Code

https://claude.ai/code/session_01HX9h9yQ3A7rkgDtZzotEPx

The unified-billing run path (env.AI.run) validates an Anthropic Messages
body's `system` as a string, but @ai-sdk/anthropic always sends an array
of text blocks. Every request with a system prompt failed with
"7003: Invalid value at system: Invalid input: expected string, received
array". Join text-only blocks into a string on the run path; the gateway
path is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HX9h9yQ3A7rkgDtZzotEPx
@changeset-bot

changeset-bot Bot commented Oct 9, 2026

Copy link
Copy Markdown

@mattzcarey

mattzcarey commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

@mattzcarey mattzcarey closed this Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants