Diagnosing Claude Code configuration, extensions, and behavior. Verified against https://code.claude.com/docs/en/debug-your-config (June 2026).
Core principle: when something you configured doesn't take effect, the cause is almost always one of three things — it didn't load, it loaded from a different location than you expect, or another scope overrode it. Inspect what actually loaded before editing anything.
| Command | Shows |
|---|---|
/context |
Everything in the context window by category: system prompt, memory, skills, MCP tools, messages |
/memory |
Which CLAUDE.md and rules files loaded, plus auto-memory |
/skills |
Available skills (project/user/plugin) with invocation badges; Space cycles skillOverrides states |
/agents |
Configured subagents and settings |
/hooks |
Every hook registered this session, grouped by event |
/mcp |
MCP servers, connection status, approval state |
/permissions |
Resolved allow/deny rules in effect |
/doctor |
Diagnostics: invalid settings keys, schema errors, skill-description budget overflow, install health. Press f to send the report to Claude for guided fixes |
/debug [issue] |
Enables debug logging and prompts Claude to self-diagnose from logs |
/status |
Active settings sources, incl. whether managed settings apply |
| Flag | Use |
|---|---|
claude --debug |
Debug logging; filter by category: --debug "api,hooks", negate: --debug "!statsig,!file" |
claude --debug hooks |
Watch hook evaluation live: events fired, matchers checked, exit codes, output |
claude --debug mcp |
MCP server stderr (e.g. connected-but-zero-tools) |
claude --debug-file /tmp/claude.log |
Write debug logs to a file (implies debug mode; beats CLAUDE_CODE_DEBUG_LOGS_DIR) |
claude --safe-mode |
(v2.1.169+) All customizations off: CLAUDE.md, skills, plugins, hooks, MCP, custom commands/agents, output styles, themes, keybindings, LSP, auto-memory. Auth/model/tools/permissions normal. Managed policy still partially applies |
claude --bare |
Minimal startup for scripts (different goal than safe-mode: speed/reproducibility, not triage) |
claude --verbose |
Full turn-by-turn output |
claude doctor / claude --version |
Install health from the shell |
claude --safe-mode — problem gone? A customization is the cause; use targeted / commands to find which.cd /tmp && CLAUDE_CONFIG_DIR=/tmp/claude-clean claude (no user/project config at all; managed settings and env vars still apply; you'll re-login on Linux/Windows).| Symptom | Cause | Fix |
|---|---|---|
Not in /skills |
File at .claude/skills/name.md instead of a folder |
Must be .claude/skills/name/SKILL.md |
Not in /skills |
New top-level skills dir created mid-session | Restart (existing dirs are live-watched; new top-level dirs are not) |
In /skills, Claude never invokes it |
disable-model-invocation: true ("user-only" badge), or description doesn't match request phrasing |
Check badge; add natural trigger keywords to description/when_to_use |
| Used to trigger, stopped | Description budget overflow (1% of context window) | /doctor shows affected skills. Raise skillListingBudgetFraction, set noise skills to "name-only" in skillOverrides, front-load key triggers (1,536-char per-skill cap) |
| Triggers too often | Description too broad | Tighten description or disable-model-invocation: true |
| Stops influencing behavior mid-session | Content usually still present; model preferring other approaches — or dropped by compaction (5k/skill, 25k combined budget) | Strengthen description/instructions, enforce with hooks, or re-invoke after compaction |
| Frontmatter ignored | YAML error (e.g. unquoted : in description) |
Quote strings; validate YAML |
Decision tree:
/hooks? It isn't being read:
"hooks" key in a settings file — there is no standalone hooks file for user/project config (only plugins use hooks/hooks.json).~/.claude.json is app state, NOT settings — hooks/permissions/env go in ~/.claude/settings.json.matcher as a JSON array is a schema error: the entry is dropped, /doctor reports it.Bash not bash; tool names are capitalized.|: "Edit|Write".mcp__server__tool or regex mcp__server__.*.Fires but misbehaves? claude --debug hooks, then test the script standalone:
echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./hook.sh; echo "exit=$?"
$TOOL_INPUT env contract is stale/dead).Settings edits apply after a brief file-stability delay — no restart needed; re-run /hooks to refresh.
.claude/agents/ (project, recursive scan), ~/.claude/agents/ (user), plugin agents/, --agents JSON, managed. Priority: managed > CLI > project > user > plugin.name frontmatter field, not the filename. Duplicate names in one scope: one silently wins./agents-created ones apply immediately).name and description are required frontmatter; delegation quality depends on description (add "Use proactively…" phrasing).hooks, mcpServers, permissionMode frontmatter (security restriction) — copy into .claude/agents/ if needed./mcp shows status. Project .mcp.json servers need one-time approval — a dismissed prompt leaves them disabled until approved from /mcp..mcp.json lives at the repo root, not inside .claude/; settings.json has no mcpServers key (use claude mcp add --scope user for user scope).command/args (resolve against launch dir) — use absolute paths./mcp; persists → claude --debug mcp for stderr.env doesn't propagate to MCP child processes — set per-server env in .mcp.json.claude plugin validate ./my-plugin # schema check; warnings for unknown fields
claude plugin validate ./my-plugin --strict # warnings become errors — use in CI
claude plugin list # what's installed/loaded
keywords as string) = load errors..claude-plugin/ holds only plugin.json (and marketplace.json); all component dirs (skills/, agents/, hooks/, commands/, output-styles/) sit at the plugin root.CLAUDE.md is NOT loaded — ship context as a skill instead.system/init event lists plugins and plugin_errors — assert on it in CI./plugin manages enable/disable; defaultEnabled: false plugins install disabled (v2.1.154+)./reload-plugins (skills text is the only live-reloaded piece)./permissions shows resolved rules. Precedence: managed always wins; then local > project > user; flags/env override files.settings.local.json silently overrides settings.json — the classic "my setting is ignored".Bash(rm *) deny matches the literal command string — /bin/rm, find -delete sail past. Hard guarantees need a PreToolUse hook or sandboxing.Bash(git diff *) vs Bash(git diff*) (also matches git diff-index).| Symptom | Likely cause |
|---|---|
| Global hooks/permissions/env ignored | Put in ~/.claude.json instead of ~/.claude/settings.json |
| settings.json value ignored | Same key in settings.local.json |
| Subdirectory CLAUDE.md ignored | Loads on demand when Claude Reads a file there, not at startup |
| Cleanup never runs at session end | No SessionEnd hook configured |
Skill at skills/name.md invisible |
Needs folder + SKILL.md |
| Hook with array matcher dropped | Matcher must be a string |
MCP under .claude/.mcp.json never loads |
Move to repo root |
--debug-file <path> pins location; CLAUDE_CODE_DEBUG_LOGS_DIR sets the directory.~/.claude/projects/<encoded-cwd>/ — greppable for tool inputs/outputs and hook activity.claude project purge [path] --dry-run previews clearing all local state for a project.