claude-code - 💡(How to fix) Fix effortLevel "max" silently dropped from settings.json, and /effort picker ignores CLAUDE_CODE_EFFORT_LEVEL [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#54249Fetched 2026-04-29 06:32:21
View on GitHub
Comments
1
Participants
2
Timeline
6
Reactions
0
Timeline (top)
labeled ×5commented ×1

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).

  2. /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.

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.

Fix Action

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).

Code Example

{ "effortLevel": "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); }

---

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

---

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

---

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
}

---

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

---

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

---

{
  "effortLevel": "xhigh",
  "env": { "CLAUDE_CODE_EFFORT_LEVEL": "max" }
}
RAW_BUFFERClick to expand / collapse

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).

  2. /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:
    { "effortLevel": "max" }
  2. Restart Claude Code on Opus 4.7.
  3. 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:
    {
      "effortLevel": "xhigh",
      "env": { "CLAUDE_CODE_EFFORT_LEVEL": "max" }
    }
  2. Restart Claude Code on Opus 4.7.
  3. 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).

extent analysis

TL;DR

The most likely fix involves updating the iK_ function to use the same validator as the env-var path and modifying the picker to resolve the displayed current selection through the db / abH path.

Guidance

  • Replace the iK_ function with the ur / gQ_ validator to ensure consistency in effort level validation.
  • Update the picker to use the db / abH path to resolve the current effort level, allowing env var overrides to be reflected in the UI.
  • Alternatively, seed effortValue from XXH() ?? A_8(w.effort) on session init to include the env var in the initial state.
  • Verify the fix by checking the effort level using /effort current and ensuring the picker displays the correct level.

Example

function iK_(H) {
  return ur(H); // use the same validator as the env-var path
}

function A_8(H) {
  let _ = ur(H);
  if (_ !== void 0) /* unpin opus-4-7 launch effort */;
  return XXH() ?? _ ?? nk1(); // seed effortValue from XXH() ?? A_8(w.effort)
}

Notes

The provided fixes assume that the ur / gQ_ validator and the db / abH path are correctly implemented and functional. Additional testing may be necessary to ensure the fixes do not introduce new issues.

Recommendation

Apply the suggested fixes to update the effort level validation and picker behavior, as this should resolve the reported issues and provide a more consistent user experience.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix effortLevel "max" silently dropped from settings.json, and /effort picker ignores CLAUDE_CODE_EFFORT_LEVEL [1 comments, 2 participants]