fix: cap thinking.budget_tokens when >= max_tokens#143
Conversation
OpenCode may set thinking.budget_tokens equal to (or greater than) max_tokens for custom models that aren't in its built-in registry. The Anthropic API requires max_tokens > thinking.budget_tokens and rejects such requests with a 400 error. Cap budget_tokens to 80% of max_tokens when the constraint is violated, matching the ratio used by OpenCode's own Anthropic provider when it correctly configures thinking.
There was a problem hiding this comment.
Pull request overview
This PR adds a defensive transformation to avoid Anthropic API 400s when thinking.budget_tokens is configured to be greater than or equal to max_tokens (a situation observed with custom model IDs outside OpenCode’s built-in model registry).
Changes:
- Parse and surface
max_tokensintransformBody’s request-body shape. - Cap
thinking.budget_tokenstoMath.floor(max_tokens * 0.8)whenbudget_tokens >= max_tokensto satisfy the API’s strict inequality requirement. - Add three unit tests covering equal/greater-than/no-op scenarios for the cap logic.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/transforms.ts | Adds runtime guard to ensure max_tokens > thinking.budget_tokens by capping the budget when invalid. |
| src/transforms.test.ts | Adds targeted regression tests for the new capping behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Thanks, will try to take a look at this tonight. |
Greptile SummaryPR #143 reviewed. One P2 finding: Confidence Score: 4/5Safe to merge; the guard correctly eliminates the observed 400 error for custom models and is well-tested. The fix is minimal and targeted. The only gap is that Math.floor(max_tokens * 0.8) can fall below the Anthropic API 1024-token floor for budget_tokens when max_tokens is very small, though any request with extended thinking and such a low max_tokens would fail regardless. Files Needing Attention: No files require special attention; the edge-case note on transforms.ts line 187 is a minor hardening opportunity, not a regression.
|
| Filename | Overview |
|---|---|
| src/transforms.ts | Adds a guard that caps thinking.budget_tokens to 80% of max_tokens when the pair would violate the Anthropic API's strict-inequality requirement; edge case where max_tokens < 1280 can still produce an out-of-range budget_tokens, though this is unlikely in practice. |
| src/transforms.test.ts | Adds three focused tests covering the equal, greater-than, and less-than cases for budget_tokens vs max_tokens; all cases are well-asserted and use realistic token values. |
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
src/transforms.ts:187
**Potential sub-1024 `budget_tokens` after cap**
The Anthropic API requires `budget_tokens >= 1024` for extended thinking to be valid, in addition to the `max_tokens > budget_tokens` constraint. If `max_tokens` is between 1 and 1279, `Math.floor(max_tokens * 0.8)` yields a value below 1024, so the request would still get a 400 error — just for a different reason. Adding a lower bound guard (e.g., `Math.max(1024, Math.floor(max_tokens * 0.8))`) would make the invariant complete.
Reviews (1): Last reviewed commit: "fix: cap thinking.budget_tokens when >= ..." | Re-trigger Greptile
| typeof parsed.max_tokens === "number" && | ||
| parsed.max_tokens <= parsed.thinking.budget_tokens | ||
| ) { | ||
| parsed.thinking.budget_tokens = Math.floor(parsed.max_tokens * 0.8) |
There was a problem hiding this comment.
Potential sub-1024
budget_tokens after cap
The Anthropic API requires budget_tokens >= 1024 for extended thinking to be valid, in addition to the max_tokens > budget_tokens constraint. If max_tokens is between 1 and 1279, Math.floor(max_tokens * 0.8) yields a value below 1024, so the request would still get a 400 error — just for a different reason. Adding a lower bound guard (e.g., Math.max(1024, Math.floor(max_tokens * 0.8))) would make the invariant complete.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/transforms.ts
Line: 187
Comment:
**Potential sub-1024 `budget_tokens` after cap**
The Anthropic API requires `budget_tokens >= 1024` for extended thinking to be valid, in addition to the `max_tokens > budget_tokens` constraint. If `max_tokens` is between 1 and 1279, `Math.floor(max_tokens * 0.8)` yields a value below 1024, so the request would still get a 400 error — just for a different reason. Adding a lower bound guard (e.g., `Math.max(1024, Math.floor(max_tokens * 0.8))`) would make the invariant complete.
How can I resolve this? If you propose a fix, please make it concise.
Summary
thinking.budget_tokensto 80% ofmax_tokenswhen OpenCode sendsbudget_tokens >= max_tokens, which the Anthropic API rejects with a 400 errorclaude-opus-4-6) not in OpenCode's built-in registry, wheremax_tokensandbudget_tokensmay be set to the same valuetransformBodyProblem
When using
claude-opus-4-6(or other custom model IDs) through OpenCode, the Go binary may setthinking.budget_tokensequal tomax_tokens. The Anthropic API requires strict inequality (max_tokens > thinking.budget_tokens) and rejects these requests:Fix
In
transformBody, after the existing model override logic, check ifbudget_tokens >= max_tokensand cap it toMath.floor(max_tokens * 0.8). The 0.8 ratio matches what OpenCode's own Anthropic provider uses when it correctly configures thinking for built-in models.Tests
Three new test cases covering:
budget_tokens == max_tokens(the observed failure case)budget_tokens > max_tokensbudget_tokens < max_tokens(no-op, left unchanged)