ScreenshotNeo

BlogHow-to

How to Debug Python Code

Reproduce the failure, read the traceback, and inspect runtime values with Python’s built-in pdb or the VS Code debugger.

By the ScreenshotNeo team4 October 20266 min read

To debug Python code, first reproduce the failure with a specific input, then read the traceback to identify the exception and the application line involved. Check expected versus actual values with a print or assertion for a simple case, or pause execution with a breakpoint and inspect values and the call stack using pdb or an IDE debugger. Make one change, then reproduce the same case to verify the fix.

1. Reproduce the failure and read the traceback

Write down the input and steps that trigger the problem. If possible, reduce the example to the smallest input and code path that still fails. A smaller reproduction means fewer values and calls to inspect.

Read a traceback from the bottom upward:

  1. Identify the exception type and message at the end, such as ZeroDivisionError or TypeError.
  2. Find the application frame and source line associated with the exception.
  3. Trace the calls above it to see how execution reached that line. The last line shown identifies where the exception surfaced; the underlying cause may be an earlier value or decision.

For a quick check, print the suspected value immediately before the failing operation. For example, print(repr(value)) can make a string’s whitespace or an empty value easier to spot. An assertion is useful when you have a concrete expectation: assert value is not None. For values that change across calls or branches, use a debugger so you can inspect execution at the moment it matters.

2. Debug at the terminal with pdb

pdb is Python’s built-in interactive source debugger. The built-in breakpoint() function enters it at the point where execution pauses. Save this example as average.py and run it with python average.py:

def average(values):
    breakpoint()
    return sum(values) / len(values)

print(average([2, 4, 6]))

At the (Pdb) prompt, try these commands:

Command What it does
p values Evaluate and print an expression in the current frame.
where Show the current stack and frame.
list Show nearby source lines.
step Run the next statement, entering a function call when possible.
next Run the next statement without stepping into a function call.
continue Resume execution until another breakpoint or program exit.
help Show debugger help; use help command for a command’s details.

To start a script under the debugger without editing it, run:

python -m pdb script.py

You can also run a module with python -m pdb -m package.module. When a command-line pdb session ends because the program exits abnormally, pdb enters post-mortem debugging automatically, letting you inspect the exception context. To enter post-mortem debugging after an exception in code you control, use pdb.pm() or pdb.post_mortem() from an exception handler.

Set a breakpoint near the suspected fault, inspect relevant local values, and step only while testing a specific idea. For example, if you suspect a list becomes empty before division, check its length before and after the call that might modify it. Remove temporary breakpoint() calls before normal or unattended runs. If a breakpoint is deliberately left in the code, set PYTHONBREAKPOINT=0 to disable the built-in breakpoint hook for that run.

3. Debug in VS Code

For a single script, select the dropdown next to Run and choose Python Debugger: Debug Python File. Click beside a line number to set a breakpoint, then start debugging. When execution pauses, inspect variables and the call stack in the debugger interface and step through the statements around the suspected fault.

For a project that needs a particular entry point or launch settings, create a launch.json configuration in the .vscode directory. Use the debugger configuration for the script or module you need to run. Make sure VS Code is using the Python environment that has the project’s dependencies installed; otherwise the program may fail before reaching your breakpoint.

Choose the interface that fits the problem. pdb works in a terminal and needs no IDE debugger setup. VS Code provides editor breakpoints and a graphical view of paused execution. For a program that has already crashed, command-line pdb can enter post-mortem mode automatically.

4. A focused debugging loop

  1. State a hypothesis. For example: “This function receives an empty list when the request has no items.”
  2. Choose the observation point. Put a breakpoint immediately before the suspected transformation or failing operation.
  3. Inspect the relevant state. Check the input, its type, and any values used in the next expression. Use where if the caller matters.
  4. Follow only the relevant execution. Use next to stay in the current function, or step when the called function is part of the hypothesis.
  5. Change one thing. Keep the original reproduction input so you can compare behavior.
  6. Run the reproduction again. Confirm the expected result and check that the correction has not introduced another exception.

A debugger shows runtime state; it does not establish that a proposed correction is right. The reproduction is the check that connects a change to the original failure.

5. Common errors and fixes

Symptom Likely cause What to do
The debugger reports NameError when evaluating a variable. The name is not defined in the current frame, or the debugger is paused in a different scope. Use where to inspect the current frame and stack. Move to the frame where the value is defined or inspect the name actually used there.
breakpoint() does not pause. The built-in breakpoint hook may be disabled, for example with PYTHONBREAKPOINT=0. Check the environment setting and remove or change it for the debugging run.
The program pauses, but stepping seems to skip code. next runs through a function call without entering it, or the suspected line is not on the reproduced path. Use step to enter a relevant call. Confirm the failing input reaches the breakpoint.
The IDE fails before reaching the breakpoint with an import error. The selected Python environment may not contain the project’s dependencies. Select the environment used for the project, then start the debug run again.
The traceback points into a library or framework. Your input or a prior application call may have put that code into an unexpected state. Read the surrounding application frames, then inspect the value passed into the library call.
The program behaves differently when run under a debugger. Timing, external state, or a changed run configuration may affect the reproduction. Keep the input and launch settings consistent. Add a focused log or assertion if the issue cannot be reproduced at a breakpoint.

6. Performance, reliability, and cost

For a small, deterministic failure, a print or assertion is often the quickest inspection. A debugger is useful when you need values at a particular point across several calls. Avoid stepping through unrelated code: set a breakpoint near the suspected fault and inspect only the state needed to test your hypothesis.

Debugging tools do not replace a reliable reproduction. If the failure depends on a particular input, preserve that input; if it depends on a sequence of calls, preserve the sequence. Re-run the same case after changing code. These workflows use Python’s standard library or VS Code; no paid debugging service is required.

7. Or skip the browser setup

If the Python task is capturing a webpage for inspection, you can use ScreenshotNeo, a website screenshot API and MCP server from Yorker Media. One GET request returns an image or PDF. See the ScreenshotNeo API documentation for request options.

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card.

8. FAQ

What should I inspect first when Python raises an exception?

Start with the exception type and message at the bottom of the traceback, then follow the application frames to the line and inputs involved.

Do I need to install pdb?

No. pdb is part of Python’s standard library.

When should I use an IDE debugger instead of pdb?

Use the interface you can work with most effectively: pdb for a terminal workflow, or the IDE debugger when editor breakpoints and a graphical view of execution are useful.

How do I know a bug is fixed?

Run the same reproduction that failed and verify its expected result. Then run relevant surrounding checks to catch regressions.

Sources