effortLevel in settings.json silently discards "max" and "ultracode"
Setting "effortLevel": "max" in settings.json is silently discarded, and the session starts at the default high with no warning that the value was rejected.
The effortLevel key is validated against enum(["low","medium","high","xhigh"]).optional().catch(void 0). Because of .catch(void 0), any value outside that enum is not a validation error: it produces no message and is coerced to undefined. But max is a real, selectable runtime effort tier. The full tier set is low, medium, high, xhigh, max, plus an ultracode tier (xhigh + workflows). So the persisted enum is a strict subset of the runtime tiers, excluding max and ultracode, and there is no supported way to persist max (or ultracode) as a launch default through settings.json.
For contrast, max is reachable at runtime two other ways, both session-only:
/effort(its own help text labelsmax"this session only").CLAUDE_CODE_EFFORT_LEVEL=max(a per-session override that saves nothing).
Both were confirmed to produce genuine max effort. Only the persisted effortLevel path drops it.
Steps to reproduce
- Set
"effortLevel": "max"in~/.claude/settings.json. - Start a new session.
Expected
Either the session launches at max, or a visible diagnostic states that max is not a persistable effortLevel value and is being ignored.
Actual
Effort is high (the default). No warning anywhere that max was rejected.
Suggested fix
Either or both:
- Emit a warning on out-of-enum values.
.catch(void 0)swallows invalid input silently; a diagnostic ("max" is not a valid effortLevel; ignoring) closes the silent-failure gap regardless of whethermaxbecomes persistable. - Resolve the persistence gap. Extend the persisted enum to accept
max(andultracode) so it can be set as a launch default, or, ifmaxis intentionally session-only, document thateffortLeveldoes not accept it and point users to/effortorCLAUDE_CODE_EFFORT_LEVEL.
Environment
Claude Code v2.1.x
Severity: low. A UX papercut plus a small feature request, not a critical bug. The silent discard is the annoying part, since nothing tells the user their setting was ignored.
3 Comments
Confirming this on 2.1.246, and adding a few things the report doesn't cover: how it presents to someone who doesn't already know about the enum, a display behaviour that makes it hard to self-diagnose, evidence that it isn't only a cold-start issue, and an argument against the low severity.
It doesn't present as a settings problem
settings.jsonhere has"effortLevel": "max". The session runshigh. But the symptom a user sees isn't "my settings key was ignored" — it's that the effort level keeps resetting. You pickmaxin the picker, work for a while, restart, and you're back onhighwith no indication anything changed.It was treated as a persistence bug here for two days before this issue turned up. The title accurately describes the cause, which is unfortunately not what someone hitting it would think to search for.
The effort control keeps displaying the selected value
This is the part that makes it impossible to self-diagnose, and it's worth separating from the settings bug because it may be a distinct one.
After a restart the effort control still displays the previously-selected level while the session runs
high. Checked directly on 25 Aug: the control read "Effort (Max)" with the slider at position 5 of 5, while that same session's request records from three seconds earlier readhigh. So the obvious way to verify the setting — looking at it — returns the wrong answer, which is presumably why this gets reported as a resetting problem rather than a settings one.A check that does work: the request records carry the resolved value.
Across one restart today, from the same session's records:
More than one trigger
Three occurrences here across two sessions, on 24 and 26 August. The one above followed an extension update. Another followed an in-editor Reload Window, and a third followed an ordinary restart. So it isn't limited to a cold start — the reload case is the awkward one, since reloading is routine advice for picking up settings changes.
On severity
Marked low as a UX papercut. Two things arguably push it higher, though neither is dramatic:
maxhas decided a task warrants it. Running that task athighproduces a plausible result at a quality level the user didn't pick, and as far as I can tell nothing surfaces that.Both suggested fixes would help
The diagnostic on out-of-enum values would have saved those two days on its own, regardless of whether
maxever becomes persistable.One note on the stopgap:
CLAUDE_CODE_EFFORT_LEVELworks, but it isn't equivalent to a persisted default — while it's set,/effortreports that the environment variable still controls the session. So it trades the reset problem for losing the runtime picker, which won't suit anyone who moves between effort levels deliberately.Confirming this is still present on v2.1.258 — the persisted
effortLevelenum is still["low","medium","high","xhigh"]with.catch(void 0), so"max"is silently coerced toundefinedwith no diagnostic.Adding a concrete use case that runs into this, plus a second layer worth flagging:
Use case: I want some projects to always run at max, while my global default stays lower. Two gaps compound and make this impossible today:
maxcan't be persisted at all througheffortLevel(this issue).modelSettings, e.g.{"claude-opus-4-8": {"effortLevel": "high"}}), not per-project — so evenxhighcan't be scoped to a single project independently of the model.So a project-level
.claude/settings.jsoncan't express "this repo runs at max (or even xhigh) while others stay at medium." A persistable, per-project effort level (includingmax) would close both gaps.Minor UX note: in the
/effortTUI, the footer offersEnter to confirm(persist) alongsides for this session only. Formax,Enterdoesn't actually stick — onlysdoes. So the picker presents a persist affordance for a value that can't be persisted, which is exactly the silent-discard behavior described here, surfaced in the UI.Agree with the suggested fix: at minimum emit a warning on out-of-enum values instead of swallowing them.
Confirming on 2.1.258, and adding one thing that I think simplifies the framing rather than adding to it.
@atahan150 describes two gaps that compound, max not being persistable through
effortLeveland effort persisting per model rather than per project. Reading the shipped schema, I do not think those are two gaps.I walked
claude-code-settings.schema.jsoninanthropic.claude-code-2.1.258-win32-x64for everyeffortLevelenum anywhere in it. There are exactly two, and they are identical:effortLevel ["low", "medium", "high", "xhigh"]
modelSettings.<model>.effortLevel ["low", "medium", "high", "xhigh"]
Neither accepts max. So max is not scoped wrongly, and it is not merely missing from the top level key. It has no persisted representation anywhere in the settings schema, and both routes drop it the same silent way.
That matters for the suggested fix. Extending only the top level enum would leave
modelSettingsstill discarding max without a diagnostic, so anyone who had set it per model would see the same behaviour and no message, on a version where the issue was marked fixed.One data point on my own setup, offered as a control rather than a finding. My
effortLevelishigh, and my session request records agree, so the enum does exactly what it says for a value inside it. The discard is only ever silent for values outside it, which is what makes it hard to notice.