effortLevel "max" silently dropped from settings.json, and /effort picker ignores CLAUDE_CODE_EFFORT_LEVEL

Resolved 💬 2 comments Opened Apr 28, 2026 by ylohnitram Closed May 5, 2026

Summary

On a model that supports max effort (Opus 4.7 / Opus 4.6 / Sonnet 4.6), there are two related bugs in the effort plumbing:

  1. settings.json rejects "max" — setting "effortLevel": "max" is silently dropped by the persisted-settings validator, even though max is a valid value everywhere else (--effort CLI flag, CLAUDE_CODE_EFFORT_LEVEL env var, /effort max interactive picker, model capability checks).
  1. /effort interactive picker ignores CLAUDE_CODE_EFFORT_LEVEL — even when the env var is correctly applied to API requests, the picker still shows the persisted settings.effortLevel value, which is misleading.

These two bugs interact in a confusing way: the documented workaround for #1 (set the env var instead) makes the API behavior correct, but #2 then makes the picker lie about the active level, so the user thinks the workaround failed.

Version

Claude Code 2.1.121 (macOS arm64).

Bug 1: settings.json rejects "max"

Steps to reproduce

  1. In ~/.claude/settings.json:

``json
{ "effortLevel": "max" }
``

  1. Restart Claude Code on Opus 4.7.
  2. Run /effort current.

Expected: Current effort level: max
Actual: falls back to model default xhigh — the persisted value is silently discarded.

Root cause

The validator used to read settings.effortLevel is whitelisted to four values and drops max:

function iK_(H) {
  if (H === "low" || H === "medium" || H === "high" || H === "xhigh") return H;
  return; // -> undefined for "max"
}
function nk1() { return iK_(I8().effortLevel); }

The canonical effort enum elsewhere in the binary is:

YB = ["low", "medium", "high", "xhigh", "max"];

Other entry points correctly accept max:

  • XXH() — env var CLAUDE_CODE_EFFORT_LEVELur() → uses gQ_(YB) allowlist
  • A_8() — interactive /effort picker selection → ur()
  • --effort CLI flag — explicit ["low","medium","high","xhigh","max"] validator
  • Per-model capability check lK_() already gates max to Opus 4.7 / Opus 4.6 / Sonnet 4.6

Suggested fix

Replace iK_ with the same validator the env-var path uses (ur / gQ_), so settings.effortLevel and CLAUDE_CODE_EFFORT_LEVEL accept the same set of values.

Bug 2: /effort picker ignores CLAUDE_CODE_EFFORT_LEVEL

Steps to reproduce

  1. Set in ~/.claude/settings.json:

``json
{
"effortLevel": "xhigh",
"env": { "CLAUDE_CODE_EFFORT_LEVEL": "max" }
}
``

  1. Restart Claude Code on Opus 4.7.
  2. Run /effort (no args, opens picker).

Expected: picker shows max selected (the live, env-resolved level).
Actual: picker shows xhigh selected (the persisted settings.effortLevel, ignoring the env var).

/effort current correctly reports max — only the picker is misleading.

Root cause

At session startup, appState.effortValue is initialized as:

effortValue: A_8(w.effort)  // w.effort = --effort CLI flag, often undefined
function A_8(H) {
  let _ = ur(H);
  if (_ !== void 0) /* unpin opus-4-7 launch effort */;
  return _ ?? nk1();  // -> iK_(settings.effortLevel), env var never consulted
}

The picker (o13) reads from appState.effortValue directly:

function e13(H) { return H.effortValue }
// picker: K = w_(e13)  -> appState.effortValue

Whereas the footer text and /effort current go through db()abH(), which does check the env var:

function abH(H, _) {
  // ...
  let O = XXH();  // CLAUDE_CODE_EFFORT_LEVEL
  // ...
  let T = O ?? (q ? K : void 0) ?? _ ?? K;
  // ...
  return T;
}

So API requests use the env-resolved value, but the picker UI shows the stale persisted value. Confusing, because the user opens /effort to verify their config and sees the "wrong" level.

Suggested fix

Have the picker resolve the displayed current selection through the same path the footer uses (db / abH), so env var overrides are reflected in the UI. Or, on session init, seed effortValue from XXH() ?? A_8(w.effort) so the env var participates in the initial state.

Workaround

Until #1 is fixed, set the env var via settings.json:

{
  "effortLevel": "xhigh",
  "env": { "CLAUDE_CODE_EFFORT_LEVEL": "max" }
}

This makes API requests use max. Until #2 is fixed, ignore the picker and verify with /effort current (which correctly reflects the env-resolved level).

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗