Skip to content

Bug + fix: SwarmClaw sends deprecated Anthropic thinking.type.enabled format, which breaks the newest Claude models (Sonnet 5 / Opus 4.8) #141

Description

@quantumconnectlabs

First, thank you for SwarmClaw. We are self-hosting it (v1.9.37, via your GHCR image) and it is a great platform.

I ran into a bug that I think is worth fixing at the source, and I have a small patch that resolves it. I would love for you to implement it in your codebase so it ships automatically with your updates, rather than each user having to rebuild a patched image every time.

The problem
When an agent uses the Anthropic provider with any thinking level active, SwarmClaw sends Anthropic this request field:
thinking: { type: "enabled", budget_tokens: N }
Anthropic has removed that format on its current models. On claude-sonnet-5, claude-opus-4-7, claude-opus-4-8, and claude-fable-5, the API returns a hard 400:
invalid_request_error: "thinking.type.enabled" is not supported for this model. Use "thinking.type.adaptive" and "output_config.effort" to control thinking behavior.
It still works on the older models (claude-sonnet-4-6, claude-opus-4-6) because those tolerate the deprecated format. The net effect is that SwarmClaw currently cannot chat on Anthropic's newest and most capable models.

It is also not avoidable from the UI. Even with an agent's reasoning depth set to "Default thinking" and a brand new chat, the chat path falls back to a "minimal" thinking level:

src/lib/server/chat-execution/stream-agent-chat.ts (around line 291): agentThinkingLevel = 'minimal'
src/lib/server/chat-execution/chat-turn-preparation.ts (around lines 683 to 684): forces thinkingLevel = 'minimal' for lightweight direct chat
So the deprecated field is always sent on chat.

Where it lives
The Anthropic request is built in src/lib/server/build-llm.ts, in the if (provider === 'anthropic') branch, inside the if (thinkingLevel) { ... } block (around line 105).

The fix (from → to)

Current:

if (thinkingLevel) {
const budgetMap = { minimal: 1024, low: 4096, medium: 8192, high: 16384 }
anthropicOpts.thinking = { type: 'enabled', budget_tokens: budgetMap[thinkingLevel] }
anthropicOpts.maxTokens = budgetMap[thinkingLevel] + 8192
}
Proposed:

if (thinkingLevel) {
// Anthropic removed thinking.type.enabled + budget_tokens on these models.
// They require thinking.type.adaptive (optionally with output_config.effort).
const ADAPTIVE_ONLY = /claude-(sonnet-5|opus-4-7|opus-4-8|fable-5|mythos-5)/i
if (ADAPTIVE_ONLY.test(model || '')) {
anthropicOpts.thinking = { type: 'adaptive' }
anthropicOpts.maxTokens = 16384
// Optional, to preserve a depth control on adaptive models:
// anthropicOpts.output_config = { effort: { minimal: 'low', low: 'low', medium: 'medium', high: 'high' }[thinkingLevel] }
} else {
const budgetMap = { minimal: 1024, low: 4096, medium: 8192, high: 16384 }
anthropicOpts.thinking = { type: 'enabled', budget_tokens: budgetMap[thinkingLevel] }
anthropicOpts.maxTokens = budgetMap[thinkingLevel] + 8192
}
}
Notes:

You are using LangChain's ChatAnthropic. Passing thinking: { type: 'adaptive' } works on a current @langchain/anthropic and @anthropic-ai/sdk. The optional output_config.effort may need to pass through your model kwargs depending on your LangChain version, so I left it commented. The core fix is simply switching to adaptive for these models, which removes the 400.
Reference: Anthropic's model migration guidance states that thinking.type.enabled and budget_tokens are removed on Opus 4.7 and later, Sonnet 5, and Fable 5, and that thinking.type.adaptive plus output_config.effort replace them.
Because the two fallbacks force a "minimal" level, fixing build-llm.ts alone resolves chat, since that is where the request field is constructed. Separately, you may want the "Default thinking" UI option to actually send no thinking field.
How to reproduce

Configure the Anthropic provider with a valid API key.
Create an agent on claude-sonnet-5 (or claude-opus-4-8).
Send it any chat message. You get the 400 above.
Switch the same agent to claude-sonnet-4-6 and it works.
I am happy to test a fix, and I can open a pull request if that is easier for you.

Thanks again for the great work.

Best regards,
Simon

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions