View on GitHub
FreeCAD Addon Academy

Debugging

When something inside an addon goes wrong, FreeCAD provides several ways to investigate, ranging from quick instrumentation through full attached-debugger sessions. This page covers the practical options in roughly increasing order of involvement.

Quick instrumentation

For a one-off “what is this value?” question, the fastest approach is to print into the Report view through FreeCAD.Console:

FreeCAD.Console.PrintMessage(f"selection has {len(FreeCADGui.Selection.getSelection())} objects\n")

See Logging & console for the full set of console functions and when each is appropriate. Quick instrumentation is appropriate when:

For anything more involved, move to one of the interactive options below.

breakpoint() and pdb

Python’s standard library provides pdb, an interactive command-line debugger, and a built-in breakpoint() that drops into it by default. In a regular Python script, calling breakpoint() halts execution at that line and presents a (Pdb) prompt for stepping, inspecting variables, and continuing.

Inside FreeCAD, the practical experience is uneven and depends on how the process was launched:

For anything beyond a single inspection, and any time you are working on Windows or relying on the embedded Python console, attach debugpy (below).

If you find a current, reliable way to drive pdb from FreeCAD’s Python console, both this page and the upstream wiki would benefit from a writeup of the steps.

Reading the Report view

When a command fails with an unhandled exception, FreeCAD writes the traceback to the Report view (View → Panels → Report view). The Report view does not pop up a dialog by default; users and addon authors alike sometimes click a button, see no visible result, and miss that the traceback is sitting in a panel they have not opened.

Habits worth forming:

Attaching VS Code with debugpy

For step-through debugging with persistent breakpoints, variable inspection, and conditional pauses, attach Visual Studio Code to FreeCAD’s Python process via the debugpy package.

One-time setup

debugpy is the Microsoft-maintained successor to the older ptvsd package. Install it into the Python environment FreeCAD imports from. The most reliable approach is to run pip from inside FreeCAD’s own Python console, so that the install lands wherever FreeCAD’s interpreter looks:

import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "debugpy"])

If your FreeCAD’s bundled Python lacks pip, install debugpy into your system Python and add that location to FreeCAD’s import path with sys.path.append.

Code-side setup

At the entry point of interest (a command’s Activated(), the workbench’s Initialize(), the start of a long-running operation), insert:

import debugpy

debugpy.listen(("localhost", 5678))
print("Waiting for debugger to attach...")
debugpy.wait_for_client()

listen() opens a TCP port (5678 is the conventional default). wait_for_client() blocks until VS Code attaches; FreeCAD’s GUI will appear frozen during this wait, which is expected behaviour.

VS Code launch configuration

In your project’s .vscode/launch.json:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Attach to FreeCAD",
            "type": "debugpy",
            "request": "attach",
            "connect": { "host": "localhost", "port": 5678 },
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}",
                    "remoteRoot": "${workspaceFolder}"
                }
            ]
        }
    ]
}

The pathMappings block tells VS Code that the file paths the debugger reports correspond to files in the workspace. When your addon is symlinked into the user Mod/ directory rather than copied, set remoteRoot to the symlink target (which FreeCAD reports as __file__) and localRoot to the workspace path where you edit. Without correct mappings, breakpoints set in VS Code will be marked “unverified” and will never trigger.

Attaching

  1. Start FreeCAD and trigger the code path that calls debugpy.listen() / wait_for_client(). FreeCAD’s GUI will freeze.
  2. In VS Code, run the Attach to FreeCAD configuration.
  3. Set breakpoints in VS Code. Execution resumes from wait_for_client() and halts at any breakpoint subsequently reached.

debugpy connections are per-session: when you stop debugging in VS Code, the connection closes, and the next breakpoint will require a re-attach. To attach repeatedly without modifying source, guard the listen() / wait_for_client() calls behind an environment variable:

import os
if os.environ.get("FREECAD_DEBUG"):
    import debugpy
    debugpy.listen(("localhost", 5678))
    debugpy.wait_for_client()

Start FreeCAD with FREECAD_DEBUG=1 set when you want to debug, and start it without when you do not.

PyCharm

PyCharm’s remote-debug workflow operates on the same principle, using either debugpy or PyCharm’s own bundled pydevd_pycharm package. The mechanical differences:

The general “freeze FreeCAD with wait_for_client(), attach the IDE, hit breakpoints” pattern is identical.

Legacy approaches

The FreeCAD wiki’s Debugging page documents older tooling: ptvsd (the predecessor to debugpy, no longer maintained), winpdb (a standalone Python GUI debugger, also unmaintained), and LiClipse with PyDev. All three still work in principle. debugpy with VS Code or PyCharm covers the same use cases with current, actively-supported tooling.

For C++ crashes inside FreeCAD itself rather than Python errors in your addon, the same wiki page’s gdb instructions remain useful. That is out of scope for addon-level debugging.

See also