Pular para o conteúdo

Debug a failing test with an agent

Este conteúdo não está disponível em sua língua ainda.

This guide shows you how to get an agent to use a debugger on a failure that print statements are not explaining.

The debug tool drives a real Debug Adapter Protocol session. A stopped frame answers every question about that moment at once — every local, every caller, and an expression evaluated in the frame’s own scope — where a print statement answers only the question you thought to ask before running.

The tool stays on the surface so the agent can ask why nothing is usable, rather than guessing. An adapter can actually start only when both of these hold:

  • a project marker in the checkout, and
  • the adapter’s binary resolving.

Ask the agent to check:

debug(op: "adapters")

It answers with what it found, or explains that an adapter needs both halves.

Language Adapter Marker Install
Dart / Flutter dart debug_adapter pubspec.yaml Ships with the SDK — nothing to do
Python python3 -m debugpy.adapter pyproject.toml, setup.py, requirements.txt pip install debugpy
Rust / C / C++ lldb-dap Cargo.toml, CMakeLists.txt Ships with LLVM
Go dlv dap go.mod go install github.com/go-delve/delve/cmd/dlv@latest

The order matters and the tool enforces it: breakpoints may be set before a session exists, because that is the sequence the protocol requires.

Ask the agent something like:

The compute function in bin/main.dart returns the wrong value. Set a breakpoint on the line that assigns product, launch it under the debugger and tell me what a and b actually are.

It will run roughly:

debug(op: "breakpoints", file: "bin/main.dart", lines: [7])
debug(op: "launch", program: "bin/main.dart")

The launch reports where it stopped:

Launched bin/main.dart on dart (session dart-1). Stopped: breakpoint. Use `stack` to see where.
debug(op: "stack")
→ #1 compute (bin/main.dart:7:3)
#2 main (bin/main.dart:2:18)

Then walk into it:

debug(op: "scopes", frame_id: 1)
→ Locals (reference 12)
debug(op: "variables", reference: 12)
→ a (int) = 6
b (int) = 7
product (int) = 42

An object with children reports [expand: reference N] — but only when there is genuinely something behind it, so an expansion the agent cannot use is never offered.

For anything not visible as a local:

debug(op: "evaluate", expression: "config.timeout.inSeconds", frame_id: 1)
What you want Op
Run to the next stop continue
Next line, same frame step_over
Into the call step_in
Out to the caller step_out
Stop a running program pause

For a wedged process rather than one you are starting:

debug(op: "attach", port: 5858)
debug(op: "terminate")

Always end the session. A debug adapter owns a stopped process holding whatever that process holds — a port, a lock, a database connection.

A variables expansion on a deep object graph is unbounded by nature — one Flutter widget reaches the whole element tree. Children per scope are capped at 50, values at 400 characters and stack frames at 30. A tool result that fills the context window is a tool that ends the run it was helping.