Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Debugging with Claude: Runtime Errors

Hello! Let's dive into our next lesson on advanced Claude Code workflows.

In our last session, we focused on the iterative debugging loop within a Test-Driven Development (TDD) context. You learned how to guide Claude to fix failing tests by feeding it the output from pytest. This is a powerful skill for ensuring code quality.

Today, we're shifting our focus from structured test failures to the more unpredictable world of runtime errors. These are the bugs that crash your application during execution—a 500 Internal Server Error in your FastAPI app, for instance. Your goal for this lesson is to learn how to methodically use Claude Code to debug these runtime errors by providing it with the essential context: logs, tracebacks, and clear reproduction steps.

This moves us from being a director in a controlled test environment to being a detective at the scene of a live application crash.

From Ad-Hoc to Systematic Debugging

When a server crashes, the first instinct might be to paste the giant wall of red text from the terminal into Claude and hope for the best. This can work for simple issues, but for complex bugs, a structured approach is far more effective. We want to transform debugging from a frantic search into a systematic investigation.

The claude-code-playbook on GitHub provides an excellent framework for this.

debugging-guide.md - claude-code-playbook - GitHub

This guide from the 'claude-code-playbook' repository introduces a powerful mental model for debugging with an AI assistant.

Please read the section titled 'Structured Debugging Methodology' and focus on the 'CLAUDE Debugging Method'. This acronym (Collect, List, Analyze, Understand, Determine, Evaluate) provides a robust framework for any debugging session, ensuring you don't miss critical steps.

The first and most crucial step in this methodology is C - Collect Evidence. A runtime error traceback is your primary piece of evidence.

FastAPI/Starlette ASGI Application Traceback with Division by Zero Error
This is a typical traceback from a FastAPI application. It's a critical piece of evidence, pointing to a `ZeroDivisionError` in `simple_log.py` on line 20. When debugging, providing this full, unedited traceback is non-negotiable.

To effectively collect and present this evidence, you need to structure your prompts. The debugging guide provides excellent templates for this.

debugging-guide.md - claude-code-playbook - GitHub

Let's continue with the same debugging guide to see how to apply this framework in practice.

Please review the following sections: 'Quick Diagnostic Checklist': Look at the 'Immediate Assessment' prompt. This is a perfect template for starting your investigation. 'Evidence Collection Templates': This section shows how to bundle all your evidence (error logs, system info, git status) into a comprehensive report for Claude. 'Interactive Debugging Sessions': Skim this to understand the flow of an interactive session, moving from isolation to hypothesis testing and solution validation. \nFocus on how these templates structure the information you provide to Claude.

Giving Claude Live Access to Your Logs

Manually copying and pasting server logs is tedious and error-prone. A much more powerful technique is to give Claude direct access to the running process. Since you have extensive experience with backend development, you'll immediately see the value in this.

You can instruct Claude to run your development server as a background task. This means Claude can "see" the server's stdout and stderr in real-time. When a request causes a crash, Claude has immediate access to the traceback without you needing to do anything.

Be a 10x Vibe Coder (Claude Code + Cursor + MCP)

In this clip from 'Be a 10x Vibe Coder', Greg Isenberg demonstrates this exact feature. It's a simple but profound shift in workflow.

Watch the segment from 00:24:02 to 00:25:11 to see how to run a server in the background. Note the '1 background task' indicator. Then, watch from 00:22:56 to 00:24:02 to learn about the ultrathink keyword, a useful tool for prompting Claude to analyze complex bugs more deeply.

By combining a structured prompt with a background task, your debugging request becomes incredibly potent:
> Run my FastAPI server in the background using 'uvicorn app.main:app --reload'. Then, I will send a request that I know causes a crash. Analyze the resulting server logs to find and fix the root cause. ultrathink

A Blueprint for Automated Debugging

To see how these concepts are applied in a real-world, automated tool, let's examine the "Code Watchdog" project. It's an AI agent built specifically for debugging Python code using Claude's API. While you won't be building this agent today, analyzing its logic provides a perfect blueprint for how to structure your own debugging prompts.

The agent automates the "Collect Evidence" step by monitoring file changes and logs. It then programmatically constructs a detailed prompt to send to Claude.

Building an LLM Python debugger agent with the new Claude 3.5 ...

This article from Weights & Biases breaks down the 'Code Watchdog' agent. We'll focus on how it formats information for Claude.

Please read the following sections: 'The Core logic': Notice how the generate_fix_with_anthropic function combines multiple pieces of context: the error log, recent console output, and the contents of relevant files. This is the model for a perfect debugging prompt. 'Understanding Code Watchdog's CLI arguments': Pay close attention to the arguments: number of context files, a custom instruction, and the log type (err or console). This demonstrates the importance of providing targeted, specific context. 'Python Debugger demo': Briefly review this to see the end-to-end flow: creating a bug, running the code to produce an error, and using the tool to generate a fix.

The key takeaway from Code Watchdog is its methodical approach to prompt engineering for debugging. It doesn't just send an error; it sends a curated package of evidence.

Your Turn: Debugging a FastAPI Runtime Error

Let's put this all together. You are working on your AI-powered Interview Platform. You have a FastAPI endpoint /users/{user_id}/stats that is supposed to calculate and return some statistics about a user's interview performance.

The code works for existing users, but when you try to access the endpoint for a newly created user who has completed zero interviews, the server crashes.

Relevant Code (app/routers/users.py):

@router.get("/{user_id}/stats")
def get_user_stats(user_id: int, db: Session = Depends(get_db)):
    user = crud.get_user(db, user_id=user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")

    completed_interviews = crud.get_completed_interviews_for_user(db, user_id=user_id)
    
    total_duration = sum(interview.duration for interview in completed_interviews)
    average_score = sum(interview.score for interview in completed_interviews) / len(completed_interviews) # Bug is here

    return {"completed_count": len(completed_interviews), "average_score": average_score}

Reproduction Steps:

  1. Create a new user in the database.
  2. Send a GET request to /users/{new_user_id}/stats.

Server Log / Traceback:

ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/path/to/venv/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py", line 428, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "/path/to/venv/lib/python3.9/site-packages/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/path/to/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/path/to/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/path/to/venv/lib/python3.9/site-packages/starlette/routing.py", line 732, in __call__
    await route.handle(scope, receive, send)
  File "/path/to/venv/lib/python3.9/site-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/path/to/venv/lib/python3.9/site-packages/fastapi/routing.py", line 237, in app
    raw_response = await run_endpoint_function(
  File "/path/to/venv/lib/python3.9/site-packages/fastapi/routing.py", line 165, in run_endpoint_function
    return await dependant.call(**values)
  File "/app/routers/users.py", line 35, in get_user_stats
    average_score = sum(interview.score for interview in completed_interviews) / len(completed_interviews)
ZeroDivisionError: division by zero

Your Task:
Based on everything you've learned, write a single, comprehensive prompt for Claude Code to diagnose and fix this runtime error. Your prompt should:

  • Reference the relevant file (@app/routers/users.py).
  • Include the full traceback.
  • Clearly state the reproduction steps.
  • Incorporate the principles from the CLAUDE debugging method (e.g., state expected vs. actual behavior).
  • (Optional but recommended) Instruct Claude to use a background task for verification.

Conclusion

You've now moved beyond simple test-fix cycles into the realm of systematic, evidence-based debugging of live application errors. This is a cornerstone of professional development and a key area where AI assistants can dramatically boost your productivity.

Key Takeaways:

  • Adopt a Systematic Framework: Use a method like Collect, List, Analyze, Understand, Determine, Evaluate to structure your debugging and avoid jumping to conclusions.
  • Provide High-Quality Evidence: A great debugging prompt includes the full traceback, relevant code files (@), clear reproduction steps, and a summary of expected vs. actual behavior.
  • Leverage Live Context: Running servers or scripts as background tasks gives Claude direct access to logs, creating a much tighter and more efficient feedback loop.
  • Think Like an Agent: Structure your prompts with the precision of an automated tool, specifying the exact context (files, logs, instructions) Claude should focus on.

Next Up:

We've mastered guiding Claude interactively. The next logical step is full automation. In our next lesson, we will explore how to run Claude Code in non-interactive "headless" mode, allowing you to create scripts that automate entire workflows, like a TDD cycle, as part of a CI/CD pipeline.

Can't find a good explanation? Sign up and we'll make it for you

Sign up