CLAUDE LABJP
2.1.281 — Claude Code reached 2.1.281 on September 23. The gateway now understands the newer Claude Desktop policy keys, and Bedrock upstreams gain assume_role and a guardrail setting10/07 — The old spellings of the Claude Desktop and Cowork managed config keys stop being accepted at 12:00 PT on October 7, thirteen days from now529 — A report describes background subagents ending mid-task on a transient 529, leaving the parent to piece together what actually survivedNEW — Bracketing PDF input tokens by page count before you send the fileRORK — Rork added Claude Opus 5.5 to its model menu on September 22, so the same model landed in several tools within one weekUNIT — When you hand off a long job, committing after each unit of work means a crash costs you one step, not the whole run2.1.281 — Claude Code reached 2.1.281 on September 23. The gateway now understands the newer Claude Desktop policy keys, and Bedrock upstreams gain assume_role and a guardrail setting10/07 — The old spellings of the Claude Desktop and Cowork managed config keys stop being accepted at 12:00 PT on October 7, thirteen days from now529 — A report describes background subagents ending mid-task on a transient 529, leaving the parent to piece together what actually survivedNEW — Bracketing PDF input tokens by page count before you send the fileRORK — Rork added Claude Opus 5.5 to its model menu on September 22, so the same model landed in several tools within one weekUNIT — When you hand off a long job, committing after each unit of work means a crash costs you one step, not the whole run
Articles/Claude Code
Claude Code/2026-06-18Intermediate

When a Broken settings.json Stops Claude Code From Starting — Safe Mode and How to Split Your Config

How to find which config layer is broken when a settings.json syntax error stops Claude Code from starting, recover in minutes, and structure your settings so an automated pipeline can't quietly break itself.

claude-code132settings9configuration10troubleshooting90

Premium Article

A single stray comma in a config file once stopped Claude Code from starting at all. If you hit that even once, it changes how you think about configuration. As an indie developer I run a pipeline that updates four sites automatically alongside my apps, and one morning the scheduled run had quietly stalled — I'd left a trailing comma in a hooks block I appended to .claude/settings.json. There was an error, but which file in which layer caused it wasn't obvious. That feeling — minutes draining away on triage — is the real trap with configuration.

Recent versions of Claude Code added a behavior where, when it detects a broken config, it isolates that single file and keeps starting in a degraded "safe mode." That's convenient, but if you don't understand why your usual permission rules suddenly aren't applying, it creates a different kind of confusion. Here I'll lay out what happens when config breaks, where to start triaging, and — more importantly — how to structure your settings so an automated workflow doesn't break itself in the first place.

What Claude Code does when config breaks

The first thing to internalize is that Claude Code's configuration isn't all-or-nothing. Settings are loaded from several files, and higher-priority ones override lower-priority ones. From highest to lowest:

PriorityTypeTypical path
1 (highest)Enterprise managed policymacOS: /Library/Application Support/ClaudeCode/managed-settings.json
Linux: /etc/claude-code/managed-settings.json
2Command-line arguments--model, etc.
3Project personal settings.claude/settings.local.json
4Project shared settings.claude/settings.json
5 (lowest)User settings~/.claude/settings.json

This layered structure is exactly why the behavior on breakage feels counterintuitive. Older builds could refuse to launch when any file was malformed; recent ones quarantine the broken config instead. When Claude Code finds a file it can't parse as JSON, it drops that one file from the load set and keeps starting in safe mode using the remaining valid settings.

The easy misreading here is "it started, so my settings must be applied." In reality, the permissions.deny and hooks you wrote in the quarantined file are being ignored entirely. In an automated context that produces a half-working state: a command you meant to deny falls back to ask and blocks on approval, or a hook you expected never fires and only a log remains. The "scheduled run quietly stalled" symptom I hit was exactly this pattern — a nasty gotcha when it strikes a production pipeline.

Whether you booted into safe mode shows up in the warning at startup and in /doctor, which tells you which settings file it flagged. That's the fastest place to look. I've written up how to read /doctor itself in Triaging config trouble in three minutes with /doctor.

Pinpointing which layer is broken

Recovery starts with identifying the broken file. There are at most five settings files, so mechanically validating each one as JSON surfaces the culprit immediately. This is the one-liner I run first: it parses each file with jq in priority order and bluntly flags the ones that fail.

# Validate every settings file that could be loaded, as JSON.
# Only broken files are reported as "INVALID".
for f in \
  "/Library/Application Support/ClaudeCode/managed-settings.json" \
  "/etc/claude-code/managed-settings.json" \
  "$PWD/.claude/settings.local.json" \
  "$PWD/.claude/settings.json" \
  "$HOME/.claude/settings.json"; do
  [ -f "$f" ] || continue
  if jq empty "$f" 2>/dev/null; then
    echo "OK      $f"
  else
    echo "INVALID $f"
    # Show the error with line/column of the break
    jq empty "$f"
  fi
done

Expected output looks like this — only the broken file is INVALID, and jq tells you exactly where parsing fell apart.

OK      /Users/you/project/.claude/settings.json
INVALID /Users/you/.claude/settings.json
jq: error (at /Users/you/.claude/settings.json:14): Expected another key-value pair at line 14, column 3

At that point the offending file and line are settled. If jq isn't installed, Python's standard library does the same job.

# Fallback validation when jq is unavailable
python3 - "$HOME/.claude/settings.json" << 'PY'
import json, sys
path = sys.argv[1]
try:
    with open(path) as fp:
        json.load(fp)
    print("OK", path)
except json.JSONDecodeError as e:
    print(f"INVALID {path}: {e}")
PY

The most common triage mistake is forgetting the high-priority enterprise managed policy exists. On a team-provisioned Mac, /Library/Application Support/ClaudeCode/managed-settings.json overrides your ~/.claude/settings.json, so when "I edited my user settings but nothing changed," suspect that top-level file first. Command-line arguments like --model also win over settings, so check whether a stale flag lingers in your launch script.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
If a settings.json syntax error has ever left Claude Code refusing to start, you'll understand what safe mode actually does and be able to recover in minutes
You'll get the exact commands and steps to pinpoint whether the broken config lives in the enterprise, user, project, or local layer
You'll be able to split and pre-validate your settings.json so an automated pipeline never ships a broken config in the first place
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
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 →

Related Articles

Claude Code2026-05-21
Claude Code Hook `command timed out`: Timeout Settings and Split-Execution Patterns That Actually Work
Fix Claude Code's `command timed out` hook failure without just bumping the timeout. Includes practical split-execution, detached background jobs, and a settings.json layout that keeps your session fast.
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-07-14
My MCP Timeout Was Being Ignored, and Every Call Died at Exactly 60 Seconds — Reclaiming Per-Server request_timeout_ms
A longer MCP tool call kept dying at exactly 60 seconds, ignoring the wait I had set. The cause: a per-server request_timeout_ms in .mcp.json that was never read, silently falling back to the default. Here is the correct placement after the fix, how to back the value out from a tool's response profile, and how to verify the cutoff yourself — with working code.
📚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