Skip to content

fix: clamp unsupported max variant for non-Opus Claude models - #2606

Merged
code-yeongyu merged 2 commits into
code-yeongyu:devfrom
RaviTharuma:fix/clamp-variant-on-non-opus-fallback
Mar 25, 2026
Merged

code-yeongyu merged 2 commits into
code-yeongyu:devfrom
RaviTharuma:fix/clamp-variant-on-non-opus-fallback

Conversation

@RaviTharuma

@RaviTharuma RaviTharuma commented Mar 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR is a narrow compatibility fix, not a general model-fallback system.

The model has already been selected.
The actual problem is that the request can still carry a variant level that the selected model does not support.

For Claude models, variant: "max" is valid on Opus 4.6, but not on Sonnet or Haiku. When that unsupported level is passed through unchanged, Anthropic rejects the request with:

level "max" not supported, valid levels: low, medium, high

So the failure is not “the wrong model was chosen”.
The failure is “the chosen model received a thinking/variant level it cannot accept”.

Problem

When an agent with variant: "max" (for example Prometheus or Atlas) moves from claude-opus-4-6 to claude-sonnet-4-6, the request keeps "max" even though Sonnet does not support that level.

That makes fallback and recovery flows brittle:

  • the selected model itself is valid
  • but the request still fails because the attached variant level is invalid for that model

Root cause

The anthropic-effort hook in src/hooks/anthropic-effort/hook.ts handled the Opus case, but skipped non-Opus Claude models:

  • Opus + variant: "max" -> injects effort=max
  • non-Opus Claude + variant: "max" -> returns early and leaves message.variant="max" untouched

That allows OpenCode to send the unsupported level straight to Anthropic.

Fix

When the selected Claude model is not Opus and the request carries variant: "max", this PR now:

  1. clamps the effort/level to "high"
  2. mutates message.variant to "high"

That keeps the request compatible with Sonnet/Haiku while preserving the intent to use the highest supported level.

Why this matters

Without this fix, fallback looks broken even when the fallback model itself is perfectly usable.
The system chooses a model that could work, then immediately breaks the request by carrying over a level that model does not support.

This PR fixes that narrow but important compatibility gap.

Scope

This PR intentionally stays small and targeted.

It does not implement a full general-purpose resolver for all model-specific settings such as:

  • variant
  • reasoningEffort
  • thinking
  • maxTokens
  • other provider/model-specific options

That broader problem should be solved separately as a dedicated settings compatibility resolver that runs after model selection and independently from model fallback.

Changes

  • src/hooks/anthropic-effort/hook.ts
    • clamps non-Opus Claude variant: "max" to "high"
    • keeps Opus behavior unchanged
  • src/hooks/anthropic-effort/index.test.ts
    • adds regression coverage for the clamping behavior

…back

When an agent configured with variant: "max" falls back from Opus to
Sonnet (or Haiku), the "max" variant was passed through unchanged.
OpenCode sends this as level: "max" to the Anthropic API, which rejects
it with: level "max" not supported, valid levels: low, medium, high

The anthropic-effort hook previously only handled Opus (inject effort=max)
and skipped all other Claude models. Now it actively clamps "max" → "high"
for non-Opus Claude models and mutates message.variant so OpenCode
doesn't pass the unsupported level to the API.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Auto-approved: The fix correctly handles variant clamping for non-Opus Claude models to prevent API errors, with appropriate test coverage and no apparent side effects.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9346bc8379

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/hooks/anthropic-effort/hook.ts Resolved
Comment thread src/hooks/anthropic-effort/hook.ts Resolved
@RaviTharuma

Copy link
Copy Markdown
Contributor Author

Update: After further testing, we found a cleaner approach that works as a standalone OpenCode plugin (.opencode/plugins/variant-clamp.js) without touching the effort hook at all — it just clamps message.variant in chat.params before the anthropic-effort hook runs:

// Anthropic: "max" is Opus 4.6 only — clamp to "high" for Sonnet/Haiku
if (message.variant === "max" && isClaude(model.providerID, model.modelID)) {
  if (!OPUS_PATTERN.test(model.modelID)) {
    message.variant = "high";
  }
}

However, the fix in this PR is still valuable as a built-in safety net so users don't need a separate plugin. The two approaches are complementary — the PR catches it in the effort hook, the plugin catches it even earlier.

Also filed anomalyco/opencode#17736 for OpenCode itself to validate thinking levels against model capabilities at the provider layer.

@RaviTharuma RaviTharuma changed the title fix: clamp variant max→high for non-Opus Claude models on fallback fix: clamp unsupported max variant for non-Opus Claude models Mar 17, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 4 files (changes from recent commits).

Requires human review: The change to mutate the raw message object by reference instead of using a copy, combined with potential over-clamping for non-Opus models, introduces possible regressions.

@RaviTharuma

Copy link
Copy Markdown
Contributor Author

@acamq please check it again :)

@code-yeongyu code-yeongyu added the triage:bug Confirmed bug with repro steps label Mar 24, 2026
@code-yeongyu
code-yeongyu merged commit 63ebedc into code-yeongyu:dev Mar 25, 2026
8 checks passed
@RaviTharuma
RaviTharuma deleted the fix/clamp-variant-on-non-opus-fallback branch September 23, 2026 19:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage:bug Confirmed bug with repro steps

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants