A wrong answer, a hung pipeline, an ignored instruction and a 4xx all present as “Claude is misbehaving”, yet they live in different layers with different fixes. Debugging at this level is layer isolation: prove which layer failed before you change anything in it, and change one variable at a time. The most common wrong move, and a favourite exam distractor, is to fix the layer you can see (the prompt) instead of the layer that failed (what loaded, what was sent, who rejected it).
The layers, and the first diagnostic for each
| Symptom | Layer | First diagnostic |
|---|---|---|
| Instruction ignored, hook silent, MCP tools or skill missing | Configuration loading | /context (memory files, MCP tools, skills), /status (setting sources), /permissions, /hooks, /mcp |
| File loaded but rule not followed | Instruction quality | Length, vagueness, conflicting files; if it must hold, enforce with a permission rule or hook |
| Works for one developer, not another | Local environment drift | claude doctor or /doctor; claude --safe-mode; clean config directory |
| 5xx, 529, 429, login or proxy errors | Provider, account, network | The Claude Code error reference, the status page, usage and spend limits |
| 4xx from your own integration | Request construction | error.type, message and request_id from the response |
| Clean transport, poor content | Model behaviour and prompt | Minimal repro, examples, an evaluation set |
Only the last row is a prompt problem. The loading row hides classic causes: a subdirectory CLAUDE.md loads when Claude reads a file there, not when it creates one; Explore and Plan subagents skip CLAUDE.md; a hook matcher written as a JSON array is a schema error that rejects the file; hooks in a standalone file and .mcp.json inside .claude/ are never read; a project MCP server stays off until approved; and Bash(rm *) does not match /bin/rm.
Reproduce, then bisect
Shrink the failure to the smallest input and configuration that still fails, and record the prompt, model, Claude Code version and settings sources. Output varies, so rerun before concluding.
To bisect configuration, launch claude --safe-mode, which disables CLAUDE.md, skills, plugins, hooks, MCP servers and custom commands and agents while leaving authentication, models, built-in tools and managed policy in place. If the problem vanishes, reintroduce one surface at a time. If it persists, point CLAUDE_CONFIG_DIR at an empty directory and start from a folder with no .claude, .mcp.json or CLAUDE.md; managed settings still apply, so read /status. To watch behaviour live use claude --debug (optionally a category such as --debug='mcp,startup') or --debug-file <path>; the hook debug log records which matchers were checked and each exit code, and MCP server stderr appears in the debug log under ~/.claude/debug/.
Non-interactive runs need extra care because a -p run shows no dialogs: it skips broken settings and continues, so run claude doctor to see what it dropped. With --output-format stream-json the system/init event lists mcp_servers, mcp_server_errors, plugins and plugin_errors, so a CI gate can fail on a non-empty error array, and system/api_retry events report the failing status and error category of each retry.
Reading API errors and request IDs
Every API error is JSON with a top-level error object holding a type and message, plus a request_id; the same value arrives in the request-id response header on every response, successful or not. Read status, error.type and message together, and log the ID on every failure.
- 400
invalid_request_error: the request is malformed or invalid (a spend limit set on the organisation or workspace can also return 400). The model never ran; fix the request. - 401
authentication_error: the key is malformed, revoked or expired. 403permission_error: the key authenticated but may not use that resource. Rotating the key fixes the first, not the second. - 402
billing_error, 404not_found_error, 409conflict_error, 413request_too_large: fix billing, the resource, the conflict or the payload. - 429
rate_limit_error: a rate limit, or a spend cap. A tier spend-cap 429 has noretry-afterheader and keeps failing until access resumes, so “just back off” is not always the answer. - 500
api_error, 504timeout_error(stream long requests), 529overloaded_error: transient on Anthropic's side.
The SDKs raise typed exceptions (catch the most specific class first, never string-match) and automatically retry connection errors, 408, 409, 429 and 5xx with backoff, twice by default, honouring retry-after. Do not stack a second retry loop on top without disabling one, and do not resend a deterministic 4xx unchanged. A stream can fail after a 200 with an SSE error event, so stream handlers need their own error path.
The Claude Code error reference groups messages into server errors, automatic retries, usage limits, authentication, network and request errors (prompt too long, tool schemas, model access). With OpenTelemetry enabled (CLAUDE_CODE_ENABLE_TELEMETRY=1) the claude_code.api_error event carries the Anthropic request_id from the response header, plus a client-generated client_request_id that exists even when a timeout or connection failure never produced a server ID, and session.id and prompt.id correlate events. Prompt text is not logged unless you opt in.
The request ID is your anchor
Capture it in application logs at the moment of failure. Without it you cannot match a user report to a server-side event, and support cannot investigate a specific call. In Python, successful response objects expose _request_id; for an exception, read the request-id header from its response.
Common exam distractor
Any answer that treats a 4xx as a model-quality problem, applies one blanket retry to every failure, or answers a 403 by rotating the API key is wrong. A 400 was rejected before the model ran; 400, 401, 403, 404 and 413 need the request or credentials changed; a rate-limit 429 and 5xx are retried with backoff, but a spend-cap 429 is not.
Escalation
Escalate when the failure reproduces on a clean configuration, or when several users see 5xx or 529 at once. Check the Claude status page first; it publishes incidents for the Claude API, Console and Claude Code among others. Then assemble a packet: Anthropic request IDs, timestamps with time zone, model and provider, Claude Code version, /status output, a minimal repro, what you ruled out, and the impact. Claude Code's /feedback command and the GitHub issue tracker cover product bugs; account and billing problems go to Anthropic support.
Mind hygiene: a /heapdump snapshot contains the full conversation and credentials, so attach only its -diagnostics.json file to a public issue. On your own platform, keep a runbook built on the triage table above and alert on 5xx and 529 rates separately from 4xx rates, because they imply different owners.