Run claude doctor from your shell, not inside Claude Code, to get a report on the installation and on every settings file it can find; run /doctor inside a session when you want a checkup that can also change things for you. The CLI’s own help puts it in one line: “Check the health of your Claude Code installation. Reads settings files in the current directory without a trust prompt. For a full checkup that can also fix issues, run /doctor in a session.” The part people search for is what it says when a settings file is wrong. Measured below: it names a malformed file and a value of the wrong type, with the file path, but says nothing at all about a misspelt key, and it exits 0 in every case.

Everything here ran on Linux with claude --version printing 2.1.299 (Claude Code), in a throwaway home folder (the page shows it as ~) with no network and no Claude Code session. The CLI reference describes the command as “Print read-only installation and settings diagnostics from the terminal without starting a session, including install health, settings-file validation errors, and Remote Control eligibility.” The doctor line of claude --help, exactly as this build prints it:

claude --version; echo "exit $?"
2.1.299 (Claude Code)
exit 0
claude --help > help.txt; echo "exit $?"
grep -A4 '^  doctor' help.txt
exit 0
  doctor                                Check the health of your Claude Code
                                        installation. Reads settings files in
                                        the current directory without a trust
                                        prompt. For a full checkup that can also
                                        fix issues, run /doctor in a session.

claude doctor versus /doctor

They share a name and not much else. claude doctor is a CLI subcommand: it prints a fixed report and returns. /doctor is typed at the Claude Code prompt, and the commands page says it will “Run a setup checkup that diagnoses installation, settings, extension, and CLAUDE.md problems and proposes fixes that Claude applies after you confirm.” The skills page adds that “Claude reports its findings first and asks for confirmation before changing anything.” Because /doctor needs a running session, and every session talks to a model, it was not run for this page:

/doctor

Not run here: /doctor only exists inside an interactive Claude Code session, and this page started none. What it covers is quoted from the docs above, not observed.

The troubleshooting page gives the rule of thumb for which to reach for: “If claude won’t start at all, run claude doctor from your shell instead.” A broken settings file is exactly the case where you want the shell version, because it never opens the session that would trip over the file.

What it checks: a clean run, line by line

Before the first doctor run the throwaway home held only the config folder (empty) and the working folder, ~/demo. That matters for the next section.

ls -A ~ ~/.claude
~:
.claude
demo

~/.claude:
claude doctor </dev/null > first.txt; echo "exit $?"
cat first.txt
exit 0
Claude Code doctor

Running: native (2.1.299)
Commit: c416f56ec7ec
Platform: linux-x64
Path: /home/loopdev/.local/share/claude/versions/2.1.299
Config install method: not set
Search: OK (bundled)
Auto-updates: disabled (set by env: CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC)
Auto-update channel: latest
Last update attempt: none recorded
Managed settings (remote): not fetched — no usable credentials for the settings fetch
Organization policy: not fetched: no API key or claude.ai sign-in for the policy lookup (apiKeyHelper keys are not used for it)

Remote Control
Remote Control requires a claude.ai subscription. Run claude auth login to sign in with your claude.ai account.
- Not signed in to claude.ai
- claude.ai subscription auth not active
- Sign-in is missing the user:profile scope
- Organization not resolved
- Feature-flag evaluation disabled (disabled by CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC)
- Remote Control availability could not be verified (no server response this session)

4 warnings found
- Native installation exists but ~/.local/bin is not in your PATH
  Fix: Run: echo 'export PATH="$HOME/.local/bin:$PATH"' >> your shell config file then open a new terminal or run: source your shell config file
- Running native installation but config install method is 'not set'
  Fix: Run claude install to update configuration
- claude command at ~/.local/bin/claude missing or broken (~/.local/bin does not exist)
  Fix: Run claude install to repair the installation.
- claude command at ~/.local/bin/claude missing or broken
  Fix: Run claude install to repair the installation.

For a full setup checkup that can also fix issues, run /doctor in a Claude Code session.

Taking it from the top, explained only from the report’s own words or the docs:

  • Running: names the install type and the version, the same 2.1.299 claude --version printed. Path: is where that version’s binary sits. The setup page explains the shape: “the native installer manages the launcher at ~/.local/bin/claude as a symlink into ~/.local/share/claude/versions/.” That is why the path ends in the version number. The path is outside the throwaway home, because the binary itself was the real one on this machine.
  • Search: OK (bundled) is about file search. The troubleshooting page tells you to “check that the Search line shows the path of your system ripgrep instead of OK (bundled)” after switching to a system ripgrep, so bundled means the copy that ships with Claude Code.
  • Auto-updates, Auto-update channel and Last update attempt are the updater. The channel latest is the default of the autoUpdatesChannel setting, which the setup page lists as “"latest", the default: receive new features as soon as they’re released”, and the same page says “Run claude doctor to see the result of the most recent update attempt.” The disabled reading has its own section below.
  • Managed settings (remote), Organization policy and the Remote Control block are about sign-in. This run had no account and no network, so each line says what it could not do. They are quoted, not interpreted, on this page.
  • 4 warnings found lists four problems, each followed by a Fix: line. Two are about PATH and are explained below. The other two say “Running native installation but config install method is ‘not set’” and suggest claude install, which this page did not run.

The report went to stdout; nothing came out on stderr, and the exit code was 0.

It wrote files on a fresh config folder

The docs call claude doctor “read-only installation and settings diagnostics”. On a config folder that was empty, it was not quite: after that one run there were three entries that had not been there before, and the npm log among them shows npm had been run as npm config get prefix.

ls -A ~ ~/.claude
grep -h 'verbose title' ~/.npm/_logs/*.log | sort -u
~:
.claude
.npm
demo

~/.claude:
.claude.json
backups
6 verbose title npm config get prefix

~/.claude gained .claude.json and an empty backups folder, and the home gained .npm, which is npm’s own log folder. The --version and --help calls before it had left ~/.claude empty, so the doctor run is what created them. Running it a second time changed its own report:

claude doctor </dev/null > clean.txt; echo "exit $?"
diff first.txt clean.txt; echo "diff exit $?"
exit 0
7c7
< Config install method: not set
---
> Config install method: unknown
27c27
< - Running native installation but config install method is 'not set'
---
> - Running native installation but config install method is 'unknown'
diff exit 1

The only difference between the first and second report is the install-method line, which went from not set to unknown once .claude.json existed. On a machine where you have used Claude Code before, .claude.json is already there, so you will see the second form. The second report is saved as clean.txt and every fault below is compared against it with diff, so each output shows exactly what changed and nothing else.

Planted fault: malformed JSON

A trailing comma, the most common way to break a hand-edited JSON file. The file goes in .claude/settings.json in the working folder, which the settings page lists as the shared project file: “Everyone working in the folder that contains it.”

mkdir -p .claude
printf '%s\n' '{ "permissions": { "allow": ["Bash(npm test)"] }, }' > .claude/settings.json
claude doctor </dev/null > broken.txt; echo "exit $?"
diff clean.txt broken.txt; echo "diff exit $?"
python3 -m json.tool .claude/settings.json; echo "json.tool exit $?"
exit 0
14a15,17
> Invalid settings
> - ~/demo/.claude/settings.json: Invalid or malformed JSON
> 
diff exit 1
json.tool exit 1

On stderr:

Expecting property name enclosed in double quotes: line 1 column 51 (char 50)

Three lines were added to the report, after the Organization policy line: a section called Invalid settings, one line naming the file, and a blank line. “Invalid or malformed JSON” is all it says. It does not give the line or the column, so the block ends with Python’s JSON checker, which does: line 1, column 51, the closing brace after the stray comma, on stderr. Doctor’s exit code stayed 0.

The settings page describes what an interactive session does with the same file: “Settings Error: a user, project, or local file has invalid JSON or a value the schema rejects.” So the session would stop and ask; doctor just lists it.

Planted fault: a misspelt key

Valid JSON, one letter missing: permisions instead of permissions.

printf '%s\n' '{ "permisions": { "allow": ["Bash(npm test)"] } }' > .claude/settings.json
claude doctor </dev/null > broken.txt; echo "exit $?"
diff clean.txt broken.txt; echo "diff exit $?"
exit 0
diff exit 0

diff exit 0: the report is byte for byte the clean one. Doctor checked the keys it knows and said nothing about one it does not, so a typo in a key name passes this check. Nothing in the report tells you that the allow rule in that file is not where Claude Code looks for it. If you edit settings by hand, the settings page offers a different check for this: “The $schema line points to the published JSON schema for Claude Code settings, which gives you autocomplete and inline validation in VS Code, Cursor, and any other editor that supports JSON schema.” Whether that schema flags an unknown key was not tested here.

Planted fault: a known key with the wrong type

permissions.allow should be a list of rules. Here it is one string, which is an easy slip when you only have one rule.

printf '%s\n' '{ "permissions": { "allow": "Bash(npm test)" } }' > .claude/settings.json
claude doctor </dev/null > broken.txt; echo "exit $?"
diff clean.txt broken.txt; echo "diff exit $?"
exit 0
14a15,18
> Invalid settings
> - ~/demo/.claude/settings.json › permissions.allow: Expected array, but received string
>   Suggested fix: Permission rules must be in an array. Format: ["Tool(specifier)"]. Examples: ["Bash(npm run build)", "Edit(docs/**)", "Read(~/.zshrc)"]. Use * for wildcards.
> 
diff exit 1

This is the most helpful output of the three. It names the file, the key path (permissions.allow), what it expected and what it got, and then prints a Suggested fix: line with the right format and three example rules. The settings reference agrees on the shape: permissions is an “object with allow, ask, deny” and more.

A broken user settings file is checked too

The help line says doctor “Reads settings files in the current directory”, but it also reads your user file. With the project file removed and a number put where model expects a string in ~/.claude/settings.json:

rm .claude/settings.json
printf '%s\n' '{ "model": 42 }' > ~/.claude/settings.json
claude doctor </dev/null > broken.txt; echo "exit $?"
diff clean.txt broken.txt; echo "diff exit $?"
exit 0
14a15,17
> Invalid settings
> - ~/.claude/settings.json › model: Expected string, but received number
> 
diff exit 1

The path in the report is ~/.claude/settings.json, the user file, which the settings page describes as applying to “You, in every project on this machine”. The settings reference gives the type doctor is enforcing: “string, a model alias or full model ID”.

The exit code does not tell you anything is wrong

Every claude doctor run on this page exited 0, including the three that listed invalid settings. If you want a script or a CI job to fail on a broken settings file, test the report’s text instead. With the user file from the last section still broken:

claude doctor </dev/null > report.txt; echo "exit $?"
if grep -q '^Invalid settings' report.txt; then echo "settings broken"; else echo "settings ok"; fi
exit 0
settings broken

The check looks for the Invalid settings heading at the start of a line, the exact text the three failing runs printed. It will not catch the misspelt key, because doctor never reports it.

The PATH warning and its Fix: line

The first warning in every report here was “Native installation exists but ~/.local/bin is not in your PATH”, followed by this Fix: line:

  Fix: Run: echo 'export PATH="$HOME/.local/bin:$PATH"' >> your shell config file then open a new terminal or run: source your shell config file

Not run here: the fix appends a line to your shell config file, and in this run the home folder was a throwaway one, so there was nothing to fix. The troubleshooting docs give the same line with a real file in place of “your shell config file”, ~/.bashrc for bash.

In this run the warning is true, and the reason is the throwaway home, not a broken install. The install troubleshooting page gives a one-line test for the folder being on your PATH; here it is, with two more lines to show where claude was really found:

echo $PATH | tr ':' '\n' | grep -Fx "$HOME/.local/bin"; echo "grep exit $?"
ls ~/.local/bin/claude; echo "ls exit $?"
command -v claude; echo "command exit $?"
grep exit 1
ls exit 2
/home/loopdev/.local/bin/claude
command exit 0

On stderr:

ls: cannot access '~/.local/bin/claude': No such file or directory

The docs’ test printed nothing (grep exit 1), because ~/.local/bin inside the throwaway home does not exist, and claude was found at the real home’s .local/bin through PATH. That same missing folder is behind the last two warnings, “claude command at ~/.local/bin/claude missing or broken”. On your own machine, an empty result from that test is the real thing, and the docs’ own wording for when it applies is: “If installation succeeded but you get a command not found or not recognized error when running claude, the install directory isn’t in your PATH.” The other suggested fix, claude install, was not run either: this page only reads, and the rules it ran under forbid installing.

Why Auto-updates says disabled (set by env)

The report printed “Auto-updates: disabled (set by env: CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC)”. That is this page’s own doing. Every command ran with CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC and DISABLE_TELEMETRY set, so that nothing would phone home. The environment variables page explains the first: “Set to any non-empty value, such as 1, to disable nonessential network traffic: auto-updates, telemetry, error reporting,” and it also “disables feature-flag fetching, which makes Remote Control and the other features that need feature-flag fetching unavailable”, which is the Feature-flag evaluation disabled line in the Remote Control block.

If you turn updates off on purpose with DISABLE_AUTOUPDATER, the setup page says to check the same line: it “shows disabled (set by env: DISABLE_AUTOUPDATER) instead of enabled.” So the name in the parentheses tells you which variable did it. If that line says disabled and you did not mean it to, take the name in the parentheses and search your shell profile and the env block of your settings files for it.

The same rules that kept this run offline are also why the config folder was a throwaway one: the run set both HOME and CLAUDE_CONFIG_DIR, which the docs describe as “Override the configuration directory (default: ~/.claude).” That way none of the reports above read or wrote a real ~/.claude.

If your question is how to write the settings file rather than how to check it, the settings.json walkthrough covers which file a setting goes in and what each scope does.

What ran here and what did not

Ran: claude --version, claude --help, and claude doctor seven times with its input from /dev/null, all in a throwaway home with no network. Not run: any Claude Code session, so /doctor is described from the docs only; the Fix: suggestions, claude install and the PATH line; and anything that signs in. The reports are shown as they came out, with only the throwaway home’s path written as ~.

← Back to all posts

Follow new posts by RSS: paste dankdev.com/feed.xml into any feed reader. It is a plain XML file — no signup, no email address, no account. Posts land there the same day they go up, and there is nothing to unsubscribe from later.

Back to the top