fix(opencode): let config opt out of Anthropic thinking blockBinding - #46820
Merged
Conversation
Contributor
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
type is "adaptive" or "enabled", on the premise that "models that do not run the check accept the field, so it is safe on every Claude". That premise does not hold. Since 1.18.26 shipped, anomalyco#46729 collects the same failure on three independent surfaces -- AWS Bedrock, Google Vertex, and Anthropic-compatible proxy endpoints -- all returning: thinking.adaptive.block_binding: Extra inputs are not permitted Probing an Anthropic-compatible endpoint directly with curl (no opencode in the path) narrows it further: thinking {"type":"adaptive"} -> OK thinking {"type":"adaptive"} + block_binding -> rejected thinking {"type":"adaptive"} + block_binding + beta header -> rejected thinking {"type":"enabled",budget_tokens} + block_binding -> OK So the field is refused specifically under thinking.adaptive, and sending thinking-binding-controls-2026-08-01 does not change that. Because the injection is unconditional there is no way out: affected users must pin 1.18.25. That is worse on managed installs, where a root-owned config wins the merge, so overriding options.thinking.type or registering a corrected provider both fail. Honour an explicit blockBinding in config instead of always overwriting it: blockBinding: false -> omit the field entirely blockBinding: {...} -> use the caller's value absent (unchanged default) -> inject ANTHROPIC_BLOCK_BINDING as today Default behaviour is untouched, so deployments that accept the field keep it. Applies to @ai-sdk/anthropic, @ai-sdk/google-vertex/anthropic, and the bedrock reasoningConfig path alike. Tests: 4 added covering opt-out on adaptive, opt-out on a think-by-default model, preserving an explicit value, and the bedrock path. Verified red/green -- 440 pass / 4 fail without the source change, 444 pass / 0 fail with it.
rekram1-node
force-pushed
the
fix/block-binding-opt-out
branch
from
September 2, 2026 17:04
a7a53ce to
02d67ae
Compare
Open
6 tasks
filipeforattini
added a commit
to reddb-io/redcode
that referenced
this pull request
Sep 5, 2026
…nomalyco#46820) Ported from anomalyco/opencode 68abdce. Co-authored-by: Aiden Cline <aidenpcline@gmail.com> Upstream: 68abdce
filipeforattini
added a commit
to reddb-io/redcode
that referenced
this pull request
Sep 5, 2026
…nomalyco#46820) Ported from anomalyco/opencode 68abdce. Co-authored-by: Aiden Cline <aidenpcline@gmail.com> Upstream: 68abdce
This was referenced Sep 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue for this PR
Closes #46729
Type of change
What does this PR do?
#46653 added
blockBindingto every Claude request whose thinking type isadaptiveorenabled. Its comment says "Models that do not run the check accept the field, so it is safe on every Claude", but that turns out not to be true — #46729 has reports of it being rejected on Bedrock, on Google Vertex, and on Anthropic-compatible endpoints:I hit this on the third of those. To work out whether it was opencode or my endpoint, I sent requests with
curldirectly, no opencode involved.thinking: {"type":"adaptive"}is accepted on its own; addingblock_bindingto it gets rejected; adding thethinking-binding-controls-2026-08-01beta header does not change that. Butblock_bindingunderthinking: {"type":"enabled", "budget_tokens": N}is accepted. So the field is only refused under theadaptivevariant, which lines up with the error naming the paththinking.adaptive.block_binding.Since the injection is unconditional there's no way for an affected user to avoid it short of pinning 1.18.25. That's worse on managed installs — where a root-owned config wins the merge, overriding
options.thinking.typedoesn't take, and registering a corrected provider doesn't either becauseenabled_providerscan't be extended from user config. So there's no escape hatch at all.The change makes
anthropicBlockBindingrespect an explicitblockBindingin config instead of always overwriting it:blockBinding: false→ omit the fieldblockBinding: {...}→ use the caller's valueANTHROPIC_BLOCK_BINDING, exactly as todayThe default path is unchanged, so deployments where the field works keep getting it and the prefix-mismatch problem #46653 fixed stays fixed. I pulled the logic into a small
applyBlockBindinghelper so the@ai-sdk/anthropic, vertex, and bedrockreasoningConfigbranches all behave the same way.Affected users can then do:
One thing I could not explain, in case it matters to you:
claude-opus-4-8is configured identically ({"thinking":{"type":"adaptive"}}) and by my reading ofanthropicBlockBindingshould also get the field — but the same endpoint that rejectsclaude-opus-5accepts it. Either I've misread howanthropicThinksByDefault/anthropicOmitsThinkinginteract, or the API only validates this for Claude 5. I'd rather flag it than pretend I know.If you'd prefer a different shape — a provider-level flag, a capability check, or retrying without the field on rejection — I'm happy to redo it.
How did you verify your code works?
Added 4 tests to
packages/opencode/test/provider/transform.test.ts: opt-out onadaptive, opt-out on a think-by-default model, preserving an explicit value rather than overwriting it, and the bedrockreasoningConfigpath.Checked red/green rather than just green — with the test changes in place but
transform.tsreverted todev, those 4 fail; with the fix they pass:All 440 pre-existing tests in that file still pass. Run with
bun test test/provider/transform.test.tsfrompackages/opencode.I also confirmed the underlying behaviour end to end outside the test suite: switching the model to
thinking: {"type":"enabled", budgetTokens}unblockedclaude-opus-5andclaude-sonnet-5(default,max, andoffvariants) against the endpoint that was failing, which is the same code path this flag now lets you control.Screenshots / recordings
N/A — not a UI change.
Checklist