Rubric
Contents — domains, guide and mocks

Debugging and error handling

CCDV-F 4.114 min read · checked 21 September 2026

Task statementDebugging and Error Handling (2.6%) — error type identification, recovery strategy selection, trace analysis to identify failure modes, and isolating problem origin between the integration layer and model output

Three layers a failure can live in

check from the top down

  1. TransportHTTP status, never reached the model
  2. ProtocolHTTP 200, but the turn is incomplete
  3. Contenta complete answer that is wrong
Find the layer before choosing the fix. The top two are your integration; only the bottom one is the model's output.

Identifying the error type

Start with the status code. The API returns a JSON body of {"type": "error", "error": {"type": …, "message": …}} together with a request_id, and the error type names the category precisely.

StatusTypeRetry?
400invalid_request_error — malformed or invalid requestNo — fix the request
401 / 403authentication_error / permission_error — credentials or scopeNo — fix the credential
404not_found_errorNo
413request_too_largeNo — send less
429rate_limit_error — rate or spend limitYes, with backoff; honour retry-after
500 / 504api_error / timeout_errorYes, with backoff
529overloaded_error — the API is temporarily overloadedYes, with backoff

The official SDKs already retry the transient set — connection errors, 429, 500, 504 and 529 — with exponential backoff, two attempts by default and configurable, and they respect a retry-after header when one is present. A hand-rolled client has to do the same. What no amount of retrying fixes is the 4xx family: a 400, 401, 403, 404 or 413 will return exactly the same result next time.

When the call succeeded but the turn did not

The second layer is the protocol. stop_reason tells you why generation stopped, and each value implies a different recovery.

stop_reasonWhat happenedRecovery
end_turnFinished naturallyUse the response
tool_useWaiting on a toolRun it, append the results, continue the loop
max_tokensHit your output limit mid-answerRaise max_tokens or continue — never treat the partial text as complete
stop_sequenceOne of your stop sequences firedRead stop_sequence to see which
pause_turnA server-side tool loop hit its iteration limitSend the assistant content back so it can continue
refusalThe model declinedRead stop_details; escalate or try a fallback — do not loop
model_context_window_exceededThe response filled the windowTreat it as truncated and trim context

Tool failures are a third kind of signal again, and the mistake is to let them escape as exceptions. A tool that fails should return its error to the model as a tool_result with is_error set, so the model can try a different argument or report honestly that it could not proceed. Raising instead ends the run for a problem the agent might well have recovered from.

Triage in one placepython
try:
    resp = client.messages.create(**kwargs)
except RateLimitError:            # 429 — transient
    sleep(backoff()); return retry()
except APIStatusError as e:
    if e.status_code in (500, 504, 529):
        sleep(backoff()); return retry()
    raise                         # 4xx: retrying changes nothing

if resp.stop_reason == "tool_use":
    return run_tools_and_continue(resp)
if resp.stop_reason in ("max_tokens", "model_context_window_exceeded"):
    return mark_truncated(resp)   # not a finished answer
if resp.stop_reason == "refusal":
    return escalate(resp)         # looping will not help

Choosing a recovery

What kind of failure is it?
  • 429, 5xx, timeout
    Retry with backoffhonour retry-after
  • 400, 401, 413
    Fix and resendsame request always fails
  • Truncated or paused
    Continue the turnraise the limit, send content back
  • Wrong but complete
    Validate and re-prompta content problem
Four families, four responses. Nothing here is a judgement call once you know which family the failure is in.

Isolating the integration layer from the model

This is the heart of the objective. A symptom that looks like a bad model answer is very often your own plumbing, and the fastest way to tell is to reproduce the exact request the model actually received. If the same messages, tools and system prompt produce a good answer in isolation, the fault is in what your code assembled or how it read the reply.

Same symptom, two causes

Integration layer

  • A tool_result missing its tool_use_id
  • The assistant turn never appended to history
  • A truncated reply read as a finished answer
  • Stale tool output the model had to trust

Model output

  • A complete answer that is wrong or invented
  • Output that ignores a stated constraint
  • Valid-looking JSON with a missing field
  • A refusal, reported in stop_reason
Both show up in a bug report as “the model gave a bad answer”. The left column is all fixable in your code, and none of it is prompt work.

Four questions settle the split quickly. Did the request contain what you think it did? Did the response carry stop_reason of end_turn? Did every tool call get a matching result? And did the tools return what they were supposed to? Only when all four hold is the answer genuinely the model's, and then you are in prompt, schema and validation territory — covered by the output-handling objective in Domain 6.

Trace analysis

Agents are non-deterministic between runs even with identical prompts, so debugging depends on what you recorded rather than on reproducing the failure on demand. Anthropic's own account of running agents in production stresses full tracing of decision patterns and interaction structures, and notes that agents are stateful: a small failure part-way through compounds, which is why durable execution and resuming from a checkpoint matter more than a clean restart.

What a usable trace records

  • Passes: request_id for every API callQuote it to support; it is in the response header and error body
  • Passes: stop_reason on every responseSeparates truncation from completion
  • Passes: Session identifier and turn numberLets you resume rather than restart
  • Passes: Tool name, input and output, per callWhere most “model” bugs actually are
  • Check: Retry events with attempt and statusA retry storm is invisible without them
  • Check: Token usage and cost per runExplains a run that stopped at a budget cap
  • Fails: Only the final answerTells you it broke, never where
Record these on every run, not only on failures — you cannot go back and add them to the run that broke.

The harnesses give you much of this for free. A Claude Code run in stream-json mode emits an event for each API retry, carrying the attempt number, the delay before the next one and an error category such as rate_limit or overloaded — so a slow run that was really a retry storm is visible rather than mysterious. An Agent SDK run ends with a result carrying the termination subtype, num_turns, cost and session_id; the subtype alone distinguishes a finished task from one that hit a turn or budget cap.

Traps the wrong answers are built from

Tempting but wrongDo this instead
Retrying a 400 or 401 with backoffFix the request or the credential; only 429 and 5xx are worth retrying.
Treating a max_tokens stop as a finished answerMark it truncated and continue or raise the limit.
Letting a tool exception escape the loopReturn the failure as a tool_result with is_error so the model can recover.
Calling a wrong answer a hallucination before checking the traceVerify the request, the stop reason and the tool results first.
Logging only the final outputRecord request ids, stop reasons, tool inputs and outputs, retries and cost on every run.

You should now be able to

  • Name the common API error types and say which are worth retrying.
  • Distinguish an HTTP error, a stop reason and a tool error, and handle each appropriately.
  • Choose a recovery strategy — retry, repair, continue, escalate — from the failure's category.
  • Isolate whether a bad answer came from the integration layer or from the model.
  • Specify what a trace must capture for an agent failure to be diagnosable after the fact.

Practice questions

Original questions written for this lesson, in the exam’s style. Answer first, then open the reasoning — every option is explained, including why the wrong ones are tempting.

  1. Question 1

    An invoice-extraction service returns JSON that is occasionally cut off mid-object. The API call returns HTTP 200, and the response has a stop_reason of max_tokens. The team has wrapped the parse in a retry with exponential backoff.

    What is wrong with the current handling?

    1. ABackoff is too short; the service needs a longer delay between attempts.
    2. BThe response is truncated, not failed, so it should be continued or given a higher max_tokens.
    3. CThe model is producing invalid JSON and the schema should be simplified.
    4. DThe request exceeded the context window and older messages must be trimmed.
    Show answer and reasoning
    1. AIncorrect. Timing is irrelevant — the same request will truncate at the same place every time.
    2. BCorrect. max_tokens is a stop reason on a successful call; retrying identically reproduces the truncation.
    3. CIncorrect. The JSON is valid up to the point generation stopped; the cause is the output limit, not the schema.
    4. DIncorrect. That would report as model_context_window_exceeded, which is a different stop reason.
  2. Question 2

    An agent in production began giving confident but wrong summaries after a deployment. The prompt did not change. A trace shows several turns where an assistant message requesting two tools is followed by a user turn with one result block.

    Where does the fault lie, and what should be done first?

    1. AThe model; add a stronger instruction about not guessing.
    2. BThe model; switch to a more capable model for these summaries.
    3. CThe integration layer; ensure each tool_use gets a matching tool_result.
    4. DThe tools; add retries inside each tool before returning.
    Show answer and reasoning
    1. AIncorrect. The instruction is worth having, but the model was reasoning over incomplete information supplied by the harness.
    2. BIncorrect. A better model given a missing tool result will still be missing the information.
    3. CCorrect. One result for two calls means the second tool's answer never reached the model — a harness bug that presents as invention.
    4. DIncorrect. There is no evidence the tools failed; the result was dropped on the way back.
  3. Question 3

    A scheduled Claude Code job has started taking far longer than usual. Exit codes are zero, the output is correct, and nothing appears in the application error log.

    What is the most useful next step?

    1. ARaise the job's turn limit so it has more room to finish.
    2. BInspect the run's retry events in stream-json output.
    3. CSwitch the job to a smaller model to reduce latency.
    4. DAdd a timeout so that slow runs fail fast and raise an alert.
    Show answer and reasoning
    1. AIncorrect. The job is finishing correctly; turns are not the constraint.
    2. BCorrect. Transparent retries are invisible in a successful run; the retry events name the cause, such as rate_limit or overloaded.
    3. CIncorrect. It changes the symptom without identifying the cause, and may degrade the output.
    4. DIncorrect. An alert on the symptom is reasonable later, but it diagnoses nothing now.

Sources

Drafted with AI assistance and checked against the sources above; expert review is in progress. Spotted an error? Tell us and it gets fixed, dated and listed on how this is written.