Debugging

Last updated on July 28, 2026

You can attach a debugger such as VS Code to your Python phase code and set breakpoints, step through execution, and inspect variables.

Phases run inside a worker process, so debugging uses debugpy over a socket rather than an interactive terminal. Uncaught exceptions and tracebacks already appear in the logs; a debugger is for stepping through code interactively.

Debug Mode

Add debugpy to your project dependencies:

uv add debugpy

Run with --debug:

tofupilot run --debug

The worker starts a debugpy listener and waits for a debugger to attach before running any phase. Debug mode also forces a single worker and disables phase timeouts, so a breakpoint pause is not interrupted.

Attach from VS Code with an "attach" configuration:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Attach to TofuPilot",
      "type": "debugpy",
      "request": "attach",
      "connect": { "host": "localhost", "port": 5678 }
    }
  ]
}

Set breakpoints in your phase files, run tofupilot run --debug, then start the attach configuration. Use --debug-port to change the port from the default 5678.

Debug mode runs a single worker, so parallel slots run sequentially while debugging. Remove --debug for normal parallel execution.

Manual Attach

You can also start debugpy yourself from inside a phase, without debug mode. This works on any version but you manage the listener, single-worker execution, and timeouts yourself.

import debugpy

# Start the listener once and wait for the IDE to attach
if not debugpy.is_client_connected():
    debugpy.listen(("localhost", 5678))
    debugpy.wait_for_client()

def check_voltage(measurements, multimeter):
    debugpy.breakpoint()
    measurements.voltage = multimeter.measure_dc_voltage()

When running manually, set execution.workers to 1 so the fixed port doesn't collide across workers, and remove any timeout on the phases you are debugging so a breakpoint pause isn't treated as a hung phase.

Do not combine a manual debugpy.listen() with --debug. Debug mode starts its own listener, and a second listen() on the same port raises an error.

How is this guide?

On this page