CLAUDE LABJP
AUTO — Managed Agents permission policies gained an auto mode. The server now weighs each call and either runs it, denies it, or pauses for approvalEVAL — Both agent.tool_use and agent.mcp_tool_use carry an evaluation field, so you can read back exactly how each call was judgedCONNECT — ant beta:sessions connect attaches your terminal to a running session. Pass --web and it serves the Console session viewer locally insteadCOWORK — Cowork v1.49585.0 moves the runtime to Electron 44, which makes macOS 13 Ventura the new minimumLIMIT — Messages held back by the five-hour usage limit are now sent automatically once the limit resets, and you can still edit or cancel them firstCONFIG — In-app warnings for deprecated managed settings began on September 10. The old spellings stop being accepted at noon Pacific on October 7AUTO — Managed Agents permission policies gained an auto mode. The server now weighs each call and either runs it, denies it, or pauses for approvalEVAL — Both agent.tool_use and agent.mcp_tool_use carry an evaluation field, so you can read back exactly how each call was judgedCONNECT — ant beta:sessions connect attaches your terminal to a running session. Pass --web and it serves the Console session viewer locally insteadCOWORK — Cowork v1.49585.0 moves the runtime to Electron 44, which makes macOS 13 Ventura the new minimumLIMIT — Messages held back by the five-hour usage limit are now sent automatically once the limit resets, and you can still edit or cancel them firstCONFIG — In-app warnings for deprecated managed settings began on September 10. The old spellings stop being accepted at noon Pacific on October 7
Articles/Claude Code
Claude Code/2026-09-11Intermediate

inferenceGatewayHeaders still works today. At noon on October 7 it falls back to a default

I read the deprecation notice that started appearing on September 10 as a spelling cleanup. After the cut-off, the old names are not ignored — they fall back to a fail-closed value or a default. Here is the old-to-new map and a script that audits your config in one pass.

Claude Code252managed-settings2configuration10migration8

On the morning of September 10 I opened Claude Code and found a notice I hadn't seen before: the organization's configuration used fields that are still accepted now but won't be for much longer. The Details dialog listed each field, its replacement, and the date it stops being accepted.

My first reading was that this was a spelling cleanup. If the names were simply changing, I figured I could do them all in one sitting sometime in October.

I had it backwards.

What happens on October 7 is not being ignored — it's falling back

The cut-off is October 7, 2026, 12:00 PM Pacific Time. The warning machinery shipped in the August 27 build, and per-user display began on September 10.

The part that's easy to misread is what happens afterward. If the old names were simply skipped over and everything kept working, then being late to fix them would cost almost nothing. That isn't the behavior. After the cut-off, a renamed key's old name falls back to its fail-closed value or its default.

Say you've been shipping trustBootstrapLocalExec: true to your fleet. On October 7 that true stops arriving. The drift is toward stricter, not looser. And if you have an invalid entry under managedMcpServers or orgPluginSettings, that connector or tool policy becomes unavailable until someone rewrites it.

So what changes on October 7 isn't how readable your config file is. It's what your defaults are from that day forward.

A deprecation with a deadline is not a naming problem. It's a defaults problem. Getting that backwards is exactly why I filed it one notch lower than it deserved.

One detail about the warning itself: disableConfigDeprecationWarnings suppresses the first showing, but the final reminder in the 24 hours before the cut-off still appears. Suppression only covers the opening notice — it doesn't make the deadline go away.

The old-to-new map, written down once

The mapping is in the Details dialog, but having a copy at hand makes the migration go faster. Watch for the rows where the value or the shape changes rather than the name.

Old spellingReplacementNote
inferenceGatewayHeadersinferenceCustomHeadersThe value must be a JSON object too
trustBootstrapLocalExectrustBootstrapDelivery
enduserAttributionendUserAttributionOnly the capital E changes
isDxtEnabledisDesktopExtensionEnabled
isDxtSignatureRequiredisDesktopExtensionSignatureRequired
inferenceGatewayAuthScheme: "sso"inferenceCredentialKind: "interactive"The value side changes
inferenceGatewayAuthScheme: "auto"Remove the keybearer is the default
Header maps as strings or listsA JSON objectApplies to otlpHeaders, otlpResourceAttributes, bootstrapHeaders
orgPluginSettings record formThe array formBuilds before 1.15200.0 read only the record form
Tool permission "ask-session""ask"Applies to builtinToolPolicy and friends
managedMcpServers[].scopesscopeOne space-separated string
oauth as a number or stringtrue or an oauth object

The orgPluginSettings row is the one that doesn't migrate cleanly in a single direction. Moving to the new form makes the config unreadable to older clients, so you need to confirm which builds are actually deployed before you touch it. It's the only row where the deadline and the version floor collide.

A window where neither path is read

Separate from the October 7 batch, the September 4 build moved one key: in served configuration, relaunchEnforcementHours moved out from under bootstrap and into lifecycle.

The awkward part is the read direction. Builds from September 4 onward no longer read the old path, and earlier builds do not read the new one. If your fleet isn't on a single version and you move the value, you open a window where neither side reads it.

During that window the value doesn't vanish — it reverts to the default. And in the same build, the default grace period before a required restart went from 1 hour to 24. That's a combination where you can go a while without noticing the value isn't landing.

There's one more layer: if a device-management profile sets any app-behavior key, that profile takes precedence over the served value for this key. Setting it only on the served side won't take effect. I nearly skipped past that line and spent a while moving the setting back and forth on my own machine before I reread it.

Audit the config with a check that can actually fail

There are too many items to catch by eye, and some of them sit several levels down in the nesting. I wrote the following script and started running config files through it. It picks up renamed keys, renamed values, shape violations, and the relocated path in one pass.

#!/usr/bin/env python3
"""Find spellings in managed settings that stop being accepted on 2026-10-07."""
import json, sys, pathlib
 
RENAMES = {  # old -> (new, note)
    "inferenceGatewayHeaders": ("inferenceCustomHeaders", "value must be a JSON object"),
    "trustBootstrapLocalExec": ("trustBootstrapDelivery", ""),
    "enduserAttribution":      ("endUserAttribution", "capital E"),
    "isDxtEnabled":            ("isDesktopExtensionEnabled", ""),
    "isDxtSignatureRequired":  ("isDesktopExtensionSignatureRequired", ""),
}
VALUE_RENAMES = {  # the value itself is the deprecated part
    "inferenceGatewayAuthScheme": {
        "sso":  'replace with inferenceCredentialKind: "interactive"',
        "auto": "drop the key (bearer is the default)",
    },
}
OBJECT_ONLY = ("inferenceCustomHeaders", "inferenceGatewayHeaders",
               "otlpHeaders", "otlpResourceAttributes", "bootstrapHeaders")
TOOL_POLICY_KEYS = ("builtinToolPolicy", "managedMcpServers", "orgPluginSettings")
 
 
def walk(node, path=""):
    """Yield (path, key, value) for every node, however deeply nested."""
    if isinstance(node, dict):
        for k, v in node.items():
            here = f"{path}.{k}" if path else k
            yield here, k, v
            yield from walk(v, here)
    elif isinstance(node, list):
        for i, v in enumerate(node):
            yield from walk(v, f"{path}[{i}]")
 
 
def audit(cfg):
    hits = []
    for path, key, value in walk(cfg):
        if key in RENAMES:
            new, note = RENAMES[key]
            hits.append((path, f"{key} -> {new}", note))
        if key in VALUE_RENAMES and isinstance(value, str):
            note = VALUE_RENAMES[key].get(value)
            if note:
                hits.append((path, f'{key}: "{value}"', note))
        if key in OBJECT_ONLY and not isinstance(value, dict) and key not in RENAMES:
            hits.append((path, f"{key} is a {type(value).__name__}",
                         "rewrite it as a JSON object"))
        if key == "orgPluginSettings" and isinstance(value, dict):
            hits.append((path, "orgPluginSettings is in record form",
                         "rewrite it as an array"))
        if key == "scopes" and "managedMcpServers" in path:
            hits.append((path, "managedMcpServers[].scopes",
                         "use scope: one space-separated string"))
        if key == "oauth" and not isinstance(value, (bool, dict)):
            hits.append((path, f"oauth is a {type(value).__name__}",
                         "use true or an oauth object"))
        if value == "ask-session" and any(t in path for t in TOOL_POLICY_KEYS):
            hits.append((path, '"ask-session"', 'use "ask"'))
    # Not part of the 10/7 batch: the path that moved on 2026-09-04
    if isinstance(cfg.get("bootstrap"), dict) and "relaunchEnforcementHours" in cfg["bootstrap"]:
        hits.append(("bootstrap.relaunchEnforcementHours",
                     "moved to lifecycle.relaunchEnforcementHours",
                     "builds from 2026-09-04 do not read the old path"))
    return hits
 
 
def main(paths):
    total = 0
    for p in paths:
        f = pathlib.Path(p)
        if not f.exists():
            print(f"[skip] {p} (not found)")
            continue
        try:
            cfg = json.loads(f.read_text())
        except json.JSONDecodeError as e:
            # Since September 4, an unparseable config blocks startup entirely
            print(f"[FATAL] {p}: not valid JSON -> {e}")
            total += 1
            continue
        hits = audit(cfg)
        print(f"\n=== {p}{len(hits)} to fix ===")
        for path, what, note in hits:
            print(f"  {path}\n    {what}" + (f"\n    {note}" if note else ""))
        total += len(hits)
    print(f"\n{total} total")
    return 1 if total else 0
 
 
if __name__ == "__main__":
    sys.exit(main(sys.argv[1:] or ["managed-settings.json"]))

Run it against a file deliberately stuffed with old spellings and you get:

=== sample-managed-settings.json — 11 to fix ===
  inferenceGatewayHeaders
    inferenceGatewayHeaders -> inferenceCustomHeaders
    value must be a JSON object
  inferenceGatewayAuthScheme
    inferenceGatewayAuthScheme: "sso"
    replace with inferenceCredentialKind: "interactive"
  ...
  managedMcpServers[0].scopes
    managedMcpServers[].scopes
    use scope: one space-separated string
  bootstrap.relaunchEnforcementHours
    moved to lifecycle.relaunchEnforcementHours
    builds from 2026-09-04 do not read the old path
 
11 total

Run the rewritten config through the same script and you get zero hits and an exit code of 0. That exit code is the whole point. A check that can't fail the build is decoration, not a check. I've written the eyeball-the-output kind before, and it took me a long time to notice it had never caught anything.

Separately from deprecation, a key name you simply misspell is still swallowed without a word. I wrote that one up in A One-Letter Typo in settings.json Is Ignored Without a Single Warning. Reading the two together makes it clearer which parts of your config are validated at all — and which parts you're simply trusting.

A broken config now blocks startup

One last change, and it sits underneath the migration work itself. Since the September 4 build, if a device's managed-settings.json, one of its drop-ins, the device-management plist, or the Windows policy registry value cannot be parsed, Claude Code refuses to start and names the source.

It used to ignore the unreadable source and start anyway. That was friendlier, and it also meant you could go indefinitely without knowing a setting wasn't taking effect. Now it stops.

Which means a single trailing comma left behind mid-migration will keep sessions on that machine from starting at all. That's why the script above bails out with [FATAL] the moment parsing fails rather than continuing:

[FATAL] broken.json: not valid JSON -> Expecting property name enclosed in double quotes: line 1 column 26 (char 25)
 
1 total

Before pushing a config change out to machines, I run it through this step every time. The difference in effort between a failure you catch on one machine and a failure you catch after distribution is not small.

Where to start

Run your own managed-settings.json through the script once and look at the count. If it's zero, you're done thinking about October 7. If anything comes back, start by working out which default that particular line falls back to after the cut-off — that tells you how urgent it actually is.

If you want to make the audit a standing habit rather than a one-off, Until I planted a failing sample, my unattended checks had never once failed goes through building checks that are verified to fail, sample and all.

Deadlines have a way of turning into "I'll batch it later." Mine did. Thanks for reading.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Claude Code2026-08-25
A One-Letter Typo in settings.json Is Ignored Without a Single Warning
I diffed claude doctor output between a settings.json with misspelled keys and a correct one. There was no difference at all. Here is what actually gets validated, what slips through, and a small check that catches typos before they cost you a day.
Claude Code2026-08-21
Adding ANTHROPIC_DEFAULT_MODEL Changes Nothing While ANTHROPIC_MODEL Is Still Set
ANTHROPIC_DEFAULT_MODEL, added in v2.1.236, is not a replacement for ANTHROPIC_MODEL. Here is how the two differ, plus a Python script that lists every place a model can be pinned.
Claude Code2026-07-19
I Could No Longer Remember What I'd Changed in Auto Mode — Where claude auto-mode reset Fits In
Running nightly automation for weeks, I kept nudging my auto-mode settings one at a time. One morning a small oddity made me realize I could not recall what I had changed. Here is how I rebuilt my configuration around claude auto-mode reset as a known-good baseline, from a solo developer's field notes.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links