Introduction to Zed Debugging
Zed is a high-performance, multiplayer code editor built in Rust by the creators of Atom. While Zed is renowned for its blazing speed and minimal footprint, it also ships with robust debugging capabilities that integrate tightly with the Debug Adapter Protocol (DAP). This guide walks you through everything you need to know to debug applications effectively inside Zed, from initial setup to advanced workflows.
What Is Zed Debugging?
Zed debugging refers to the set of features inside the Zed editor that allow you to launch, attach to, and inspect running programs. Rather than reinventing the wheel, Zed leverages the Debug Adapter Protocol — the same open standard popularized by VS Code — to communicate with language-specific debug adapters. This means that if a debug adapter exists for your language, you can almost certainly use it inside Zed.
At its core, Zed debugging lets you set breakpoints, step through code, inspect variables, evaluate expressions, watch call stacks, and view runtime output — all without leaving the editor window.
Why Debugging in Zed Matters
- Speed: Zed's native Rust core means the debugger UI stays responsive even with large projects.
- Context retention: You debug in the same window where you write code, preserving mental context.
- Multiplayer support: Collaborators can observe the debugging session in real time.
- Standardized protocol: DAP compatibility means a consistent experience across languages.
- Minimal configuration: Zed's debug configuration is concise and project-scoped.
Prerequisites and Setup
Before you can debug in Zed, you need three things: the Zed editor itself (stable or preview build), a debug adapter for your target language, and a project with a valid debug configuration file.
Installing a Debug Adapter
Zed does not bundle debug adapters. Instead, it downloads them on demand the first time you launch a debug session for a given language. For example, debugging a Python project will trigger Zed to fetch the debugpy adapter. You can also pre-install adapters manually if you prefer offline workflows.
Common adapters include:
- Python:
debugpy - JavaScript/TypeScript (Node):
js-debug(vscode-js-debug) - Rust:
codelldborlldb-dap - Go:
delve(dlv) - C/C++:
codelldborcppdbg
Creating a Debug Configuration
Zed stores debug configurations in a .zed/debug.json file at the root of your project. This file defines one or more named "launch configurations" that describe how to start or attach to a program. Here is a minimal example for a Node.js project:
[
{
"label": "Run Current File",
"adapter": "JavaScript",
"program": "$ZED_FILE",
"request": "launch",
"type": "pwa-node"
}
]
Each entry in the array is a separate configuration you can select from the debug panel. The label is what appears in the UI, the adapter tells Zed which debug adapter to use, and the remaining fields mirror the DAP launch request payload.
Launching Your First Debug Session
Once your .zed/debug.json file is in place, you can start debugging. Open the debug panel using the command palette (cmd-shift-p on macOS, ctrl-shift-p on Linux) and search for "debugger: toggle". Alternatively, use the keyboard shortcut cmd-shift-d / ctrl-shift-d.
Step-by-Step Workflow
- Open the file you want to debug.
- Click in the gutter next to a line number to set a breakpoint (a red dot appears).
- Open the debug panel and select your launch configuration from the dropdown.
- Click the green play button to start the session.
- When execution pauses at your breakpoint, use the step controls to navigate.
- Inspect variables in the variables pane, or hover over symbols in the editor.
- Click stop to terminate the session.
Working with Breakpoints
Breakpoints are the foundation of any debugging workflow. Zed supports several breakpoint types beyond the simple line breakpoint.
Standard Breakpoints
Click the gutter to toggle a breakpoint on a line. You can also use F9 when the cursor is on the desired line. Breakpoints persist across sessions as long as the file remains in the project.
Conditional Breakpoints
Right-click the gutter and choose "Edit Breakpoint" to add a condition or hit count. A conditional breakpoint only pauses when the expression evaluates to true. This is invaluable for debugging loops:
// Break only when the counter exceeds 1000
counter > 1000
// Break only on every 50th hit
hitCount % 50 == 0
Logpoints
Logpoints let you print messages to the debug console without modifying your source code. They appear as diamond-shaped icons in the gutter. Use them when you want to trace execution in production-like scenarios where editing files is undesirable.
Stepping Through Code
Once paused at a breakpoint, Zed provides the standard stepping controls:
- Continue (F5): Resume execution until the next breakpoint.
- Step Over (F10): Execute the current line without descending into functions.
- Step Into (F11): Descend into the next function call on the current line.
- Step Out (shift-F11): Run until the current function returns.
- Restart (ctrl-shift-F5): Stop and relaunch the current configuration.
- Stop (shift-F5): Terminate the debug session.
These shortcuts can be customized in your keymap.json file if you prefer different bindings.
Inspecting State
Variables Pane
The variables pane displays all in-scope local variables, arguments, and closures. Expand objects and arrays to drill into nested structures. Right-click any variable to copy its value or add it to the watch list.
Watch Expressions
For values you want to monitor across multiple steps, add them to the watch pane. Watch expressions are re-evaluated after every pause, making them ideal for tracking derived state:
users.filter(u => u.active).length
request.headers["authorization"]
state.todos[0].completed
Call Stack Pane
The call stack pane shows the chain of function calls that led to the current pause point. Click any frame to jump to that location in the source and inspect its local scope. This is essential for understanding how you arrived at an unexpected state.
Debug Console / REPL
The debug console lets you evaluate arbitrary expressions in the context of the paused frame. Use it to test hypotheses, call methods, or mutate state on the fly:
> user.validate()
true
> user.email = "test@example.com"
"test@example.com"
> db.query("SELECT COUNT(*) FROM orders")
{ count: 42 }
Language-Specific Examples
Debugging Python
For a Python project, your .zed/debug.json might look like this:
[
{
"label": "Python: Current File",
"adapter": "Python",
"request": "launch",
"program": "$ZED_FILE",
"console": "integratedTerminal",
"justMyCode": true
}
]
Ensure debugpy is installed in your environment:
pip install debugpy
Debugging Rust
Rust debugging typically uses codelldb. Build your binary with debug symbols and configure Zed accordingly:
[
{
"label": "Debug Rust Binary",
"adapter": "LLDB",
"request": "launch",
"program": "${workspaceFolder}/target/debug/my_app",
"stopOnEntry": false
}
]
Compile with:
cargo build
Debugging Go
Go uses Delve. Install it first:
go install github.com/go-delve/delve/cmd/dlv@latest
Then configure Zed:
[
{
"label": "Debug Go Package",
"adapter": "Delve",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}"
}
]
Attaching to a Running Process
Sometimes you need to debug a process that is already running — for example, a web server started by a container orchestrator. Use "request": "attach" instead of "launch":
[
{
"label": "Attach to Node Process",
"adapter": "JavaScript",
"request": "attach",
"processId": "${command:pickProcess}",
"type": "pwa-node"
}
]
For Python, start the target process with debugpy in listen mode:
python -m debugpy --listen 5678 --wait-for-client my_script.py
Then attach:
[
{
"label": "Attach to Python on 5678",
"adapter": "Python",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5678
}
}
]
Best Practices
- Commit your debug.json: Share launch configurations with your team by committing
.zed/debug.jsonto version control. Use environment-specific overrides for secrets. - Use conditional breakpoints in loops: Avoid manually clicking continue hundreds of times. A well-placed condition saves enormous time.
- Leverage logpoints in shared environments: When pair programming in Zed's multiplayer mode, logpoints let teammates see trace output without modifying files.
- Keep adapters updated: Debug adapters receive frequent bug fixes. Periodically clear Zed's adapter cache to force re-download of the latest version.
- Disable justMyCode selectively: In Python, set
"justMyCode": falsewhen you need to step into library code. - Use stopOnEntry for unfamiliar codebases: Pausing on the first line gives you a chance to set up watches before execution proceeds.
- Combine with Zed's tasks: Define build tasks in
.zed/tasks.jsonand chain them before debug launches to ensure you always debug fresh binaries. - Learn the keyboard shortcuts: Muscle memory for stepping controls dramatically improves debugging throughput.
Troubleshooting Common Issues
Breakpoints Not Hit
If your breakpoints appear as hollow gray circles instead of solid red dots, the debug adapter cannot map the breakpoint to executable code. Common causes include stale build artifacts, source maps pointing to outdated files, or optimizations stripping debug symbols. Rebuild your project and ensure debug info is enabled.
Adapter Fails to Download
Behind a corporate proxy or firewall, Zed may be unable to fetch adapters automatically. Set the HTTPS_PROXY environment variable before launching Zed, or pre-install the adapter manually and point Zed to it via your settings.
Variables Show "Not Available"
This usually means the program was compiled with optimizations that inline or eliminate variables. Recompile with -O0 (C/C++), --debug (Rust), or remove minification (JavaScript) to preserve variable info.
Conclusion
Debugging in Zed combines the speed and simplicity of a lightweight editor with the full power of the Debug Adapter Protocol. By mastering breakpoints, stepping controls, variable inspection, and language-specific configurations, you can diagnose issues faster and with greater confidence. As Zed continues to evolve, its debugging story will only grow richer — but the fundamentals covered here will remain the backbone of an efficient workflow. Start with a simple launch configuration, get comfortable with the keyboard shortcuts, and gradually incorporate advanced techniques like conditional breakpoints, logpoints, and attach mode as your needs expand.