debugging-reference.md 9.0 KB

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:

    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

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 <path> pins location; CLAUDE_CODE_DEBUG_LOGS_DIR sets the directory.
  • Session transcripts (JSONL) live under ~/.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.