On this page
TLDR
A Claude application fails in two structurally different places: the integration layer (the call never reached a valid model turn - bad auth, malformed request, rate limit) or model output (the call returned 200, but the content is wrong - refused, truncated, or malformed). The error object only exists on non-2xx responses; a 200 with a bad stop_reason is not an HTTP error, so your code has to detect it itself.
What you are deciding
Before fixing an LLM failure, decide which layer owns it: transport, request configuration or model output. Replay the recorded request several times, compare outcomes, and only then assign a cause.
Does this failure belong to transport, request configuration or the model's output?
What it is
A production Claude call can fail in two structurally different places, and each needs a different fix. The integration layer covers everything before a valid model turn happens: bad auth, a malformed request, a rate limit, or a transient overload - these surface as non-2xx HTTP status codes with a typed error object (type, message, request_id). Model output failures are different in kind: the call returned 200, was billed, and completed - but the content itself is wrong, refused, truncated, or malformed. Confusing the two leads to the most common production debugging mistake: wrapping every call in a generic retry loop and shipping a truncated or refused response as if it were valid.
Origin isolation - deciding which of these two layers a given failure belongs to, before writing a fix - is the core skill this objective tests. It composes with two further steps: identifying the specific error TYPE once the layer is known, and selecting a recovery strategy that matches that type (retry with backoff, fix the request, raise max_tokens, adjust the prompt/policy, or enforce structure) rather than applying one generic reaction to every failure.
How each certification frames it
CCD-F: How CCD-F tests debugging
CCDVF-D4-O1Debugging and Error Handling: debugging and error handling techniques for Claude applications, including error type identification, recovery strategy selection, trace analysis to identify failure modes, and problem origin isolation between the integration layer and model output
The Developer guide lists error type identification, recovery strategy selection, trace analysis and problem origin isolation between the integration layer and model output (PDF page 7, Domain 4). The tested move is reproducing before explaining.
The API errors documentation lists status codes with error types, adds a request id to every response and error body, and says the official SDK retries transient failures with exponential backoff. It also notes that a streaming response can fail after a 200 status, with the error arriving as an event.
How it works
Typed, predictable HTTP codes drive the first triage split. 400 invalid_request_error, 401 authentication_error, 402 billing_error, 403 permission_error, 404 not_found_error, 409 conflict_error, and 413 request_too_large are all non-retryable - the request itself is broken, so blind retry just repeats the failure. 429 rate_limit_error, 500 api_error, 504 timeout_error, and 529 overloaded_error are transient - retry with backoff genuinely helps. Getting this split backwards (retrying a 400, or treating a 429 as a code bug) is the most-tested distractor pattern.
SDKs retry automatically, but only within that transient set, and only twice by default. Connection errors, 429s, and 5xx responses are retried with exponential backoff honoring retry-after, configurable via a max_retries option per client. 429 responses also expose x-ratelimit-remaining-* headers for proactive throttling before a limit is hit. Catch typed SDK exceptions (e.g. Python anthropic.NotFoundError, anthropic.RateLimitError, anthropic.APIStatusError for any other non-2xx, anthropic.APIConnectionError for a network failure before any response existed) rather than string-matching error messages, and handle the most specific exception first. Every response - success or failure - carries a request-id for support correlation, the primary trace artifact for isolating a failed call.
Model-output failures live inside a 200 and surface only through `stop_reason`. stop_reason: "refusal" means Claude declined for safety - the call is 200 and billed, and the output may not match your schema because the refusal message takes precedence over schema constraints. stop_reason: "max_tokens" means truncation - the response may be incomplete and not match your schema either. Neither raises an exception; both require branching on stop_reason before the payload is trusted, which is precisely what a try/except around typed exceptions alone will not catch.
Structured Outputs and Strict Tool Use close the remaining JSON-quality gap, but they are two separate mechanisms, not one setting. Structured Outputs is configured via output_config.format on the request and constrains the model's final text response to a JSON Schema. Strict Tool Use is configured with a top-level strict: true field on an individual tool definition (alongside name, description, and input_schema) and constrains that tool's tool_use.input to its schema. output_config.format does not take a tool-level strict: true parameter - they are configured independently, on different objects, for different output paths (final response vs. a specific tool call), even though both compile to grammar-constrained decoding through the same underlying pipeline. Neither overrides a refusal or rescues a truncated response. One residual gap even under strict mode: enum/const values can differ only in leading-word capitalization with no error and no distinguishing stop_reason, so compare enum output case-insensitively rather than assuming exact-match. Exceeding schema complexity limits (20 strict tools per request, 24 optional params, 16 union-typed params) pushes the failure back to the integration layer entirely - a 400 before generation ever starts.
Origin isolation also covers prompt-design and model-selection failures, one level above HTTP/`stop_reason` mechanics. Not every wrong answer is a refusal or truncation - a well-formed, schema-compliant 200 can still be a hallucination (confident fabrication with no refusal signal), a prompt failure (ambiguous instructions producing a technically valid but off-target answer), or a model mismatch (a lighter/cheaper model handling a task past its reliability ceiling for that complexity). None of these raise a typed exception or an unusual stop_reason - they surface only through evaluation against ground truth or ground-truth comparison against a stronger model, which is why an architect's diagnostic toolkit extends past dev-level error handling into eval-driven root-causing.

Worked example: a refund summary that sometimes comes back malformed
Authored teaching example, not a bank question.
Users report that the assistant sometimes returns a malformed refund summary. The team has a captured request.
| Replay result | Likely layer | Next step |
|---|---|---|
| Some runs return an HTTP error | Transport or request | Read the error type and retry only transient errors |
| Every run returns the same wrong output | Request configuration or input | Inspect the recorded request for template or parameter faults |
| Status is 200 but output varies between runs | Model output variability | Tighten the instruction and add output validation |
| One run passes | Inconclusive | Keep replaying before closing the issue |
The team learns which layer to fix and can prove it by rerunning the same replay afterwards. Check that the fix is verified on repeated runs, not a single success.
How this decision goes wrong
One successful replay closing the issue
Looks right: It worked on the second try, so it was a blip.
Why it fails: Model output varies between runs, so one pass proves little about the failure rate.
Instead: Replay repeatedly and compare outcomes before assigning a cause.
Changing versions during reproduction
Looks right: A newer model might fix it, so switch while testing.
Why it fails: You can no longer tell whether the failure moved because of the fix or the change. The models overview describes model IDs as pinned snapshots, so the recorded ID lets you reproduce exactly.
Instead: Replay with the recorded model ID and parameters first, and test changes afterwards as separate experiments.
Blanket retries
Looks right: Retrying every failure makes the system robust.
Why it fails: A request-validation error returns the same result every time, and retries add load and cost. Only transient errors are worth retrying.
Instead: Classify by error type: retry transient failures with backoff, fix invalid requests, and read the request id when escalating.
Where you'll see it
Ticket-extraction pipeline under load
Triage order: (1) did the call raise RateLimitError/APIStatusError - integration layer, fix with backoff/queuing; (2) if 200, read stop_reason first - max_tokens means raise the budget, refusal means fix policy/prompt, not retry; (3) only if stop_reason is end_turn/tool_use and JSON still fails to parse is it a genuine content-quality issue, fixed with output_config.format or strict: true.
Non-technical practitioner triage
A delivery consultant gets a vague, off-target answer from a client-facing Claude workflow - the CCAOF-D7-O1 framing treats this as a prompt-iteration problem (add specificity, examples, explicit format) before escalating it as a system-reliability incident.
Side-by-side
| Failure class | HTTP status | Signal | Fix approach |
|---|---|---|---|
| Integration - transient | 429 / 500 / 504 / 529 | Typed error, retryable | Retry with backoff (SDK does this automatically, twice by default) |
| Integration - non-retryable | 400 / 401 / 402 / 403 / 404 / 409 / 413 | Typed error, permanent | Fix the request/auth/schema; blind retry repeats the failure |
| Model output - refused | 200 | stop_reason: "refusal" | Not an API error; adjust policy/prompt, don't retry the same call |
| Model output - truncated | 200 | stop_reason: "max_tokens" | Raise the token budget; response may not match schema |
| Model output - malformed/non-compliant | 200 | end_turn/tool_use but JSON fails to parse or match schema | output_config.format / strict: true; compare enums case-insensitively |
How each cert tests this
CCD-F
Developer (D4 · Evaluation, Testing & Debugging, CCDVF-D4-O1): tests the mechanical triage - typed HTTP error identification, retryable-vs-not classification, SDK exception handling, trace correlation via request_id, and isolating a failure to the integration layer vs. the model-output layer via stop_reason.
CCA-P
Architect - Professional (D4 · Evaluation, Testing & Optimization, CCARP-D4-O4): tests diagnosing system issues ONE LEVEL UP from HTTP mechanics - is a wrong-but-well-formed 200 a prompt failure (ambiguous instructions), a hallucination (confident fabrication), or a model mismatch (wrong model tier for the task's complexity)? None of these raise an exception or an unusual stop_reason.
CCA-A
Associate - Foundations (D7 · Troubleshooting & Optimization, CCAOF-D7-O1): tests the non-technical entry point into the same tree - recognizing that a vague, off-target, or poorly-formatted output is usually a PROMPT problem to iterate on (more specificity, examples, explicit format), not a system error requiring an engineering escalation.
The exam trap
Treating every failure as retryable, or every malformed output as "hallucination" needing a prompt rewrite. In fact: (a) 400/401/403/404/413 never succeed on blind retry; (b) a 200 with stop_reason: "refusal" or "max_tokens" isn't an HTTP error, so code that only wraps calls in try/except around typed exceptions will silently ship truncated or refused content as valid; (c) structured outputs remove JSON-syntax failures but not schema compliance under refusal or truncation. The full objective - problem origin isolation between the integration layer and model output - is exactly this three-way split, not a single bucket.

Question patterns
CCD-F practice questions
5 approved CCD-F questions for this topic. Tap an answer to check it, then read the rationale for every option.
A production Claude call returns HTTP 200, but the JSON payload is truncated mid-object and fails to parse. The on-call engineer opens a ticket titled "API error: malformed JSON from Anthropic." Is this framing accurate?
CCA-P practice questions
No approved public CCA-P sample question is published for this topic yet. The worked example above is an authored teaching example.
CCA-A practice questions
No approved public CCA-A sample question is published for this topic yet. The worked example above is an authored teaching example.
Frequently asked
Showing 5 of 5 questions
stop_reason and, beyond that, evaluate content quality separately.stop_reason inspected before the payload is trusted; exception handling and stop_reason branching cover different failure layers.Sources and next steps
- Claude Certified Developer - Foundations Exam Guide v1.0 (July 2026), PDF page 7, section: Domain 4: Debugging and Error Handling; Domain 6: Context Engineering. Checked 2026-10-16.
- Claude API errors (Claude API docs), section: HTTP errors; Error shapes; Request ID. Checked 2026-10-16.
- Models overview (Claude API docs), section: Compare models: notes on Claude API ID and Retirement. Checked 2026-10-16.
