# Debugging Reference 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. ## Inspection Commands (run these first) | 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 | ## CLI Diagnostic Flags | 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 | ### Bisection workflow 1. `claude --safe-mode` — problem gone? A customization is the cause; use targeted `/` commands to find which. 2. Still broken? Fully clean session: `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). 3. Reintroduce config one piece at a time. ## Skill Not Working | 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 | ## Hook Not Firing Decision tree: 1. **Not listed in `/hooks`?** It isn't being read: - Hooks belong under the `"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. 2. **Listed but never fires?** Matcher problems: - Case-sensitive: `Bash` not `bash`; tool names are capitalized. - Multiple tools = one string with `|`: `"Edit|Write"`. - MCP tools need `mcp__server__tool` or regex `mcp__server__.*`. 3. **Fires but misbehaves?** `claude --debug hooks`, then test the script standalone: ```bash echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./hook.sh; echo "exit=$?" ``` - Script must read **stdin JSON** (the `$TOOL_INPUT` env contract is stale/dead). - Exit 2 + stderr = block; JSON decisions only parsed on exit 0 stdout. - Not executable / wrong shebang / CRLF line endings on Unix. 4. Settings edits apply after a brief file-stability delay — no restart needed; re-run `/hooks` to refresh. ## Agent Not Found / Not Used - Locations: `.claude/agents/` (project, recursive scan), `~/.claude/agents/` (user), plugin `agents/`, `--agents` JSON, managed. Priority: managed > CLI > project > user > plugin. - Identity comes from the `name` **frontmatter field**, not the filename. Duplicate names in one scope: one silently wins. - Files added on disk load at **session start** — restart after manual edits (`/agents`-created ones apply immediately). - `name` and `description` are required frontmatter; delegation quality depends on `description` (add "Use proactively…" phrasing). - Plugin agents ignore `hooks`, `mcpServers`, `permissionMode` frontmatter (security restriction) — copy into `.claude/agents/` if needed. - Explore/Plan built-ins skip CLAUDE.md — restate critical instructions in the delegating prompt or agent body. ## MCP Server Issues - `/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). - Failed to start: usually relative paths in `command`/`args` (resolve against launch dir) — use absolute paths. - Connected but zero tools: Reconnect from `/mcp`; persists → `claude --debug mcp` for stderr. - Server missing env vars: settings `env` doesn't propagate to MCP child processes — set per-server `env` in `.mcp.json`. ## Plugin Issues ```bash 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 ``` - Unrecognized plugin.json fields = warnings (plugin still loads); wrong **types** (e.g. `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**. - Plugin `CLAUDE.md` is NOT loaded — ship context as a skill instead. - In headless: the stream-json `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+). - Plugin component changes need `/reload-plugins` (skills text is the only live-reloaded piece). ## Permission Surprises - `/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. - Prefix rules: trailing space matters — `Bash(git diff *)` vs `Bash(git diff*)` (also matches `git diff-index`). ## Common Causes Cheat Sheet | 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 Logs - `--debug-file ` pins location; `CLAUDE_CODE_DEBUG_LOGS_DIR` sets the directory. - Session transcripts (JSONL) live under `~/.claude/projects//` — greppable for tool inputs/outputs and hook activity. - `claude project purge [path] --dry-run` previews clearing all local state for a project.