Debugging in VS Code: Breakpoints and launch.json
Practice4 minInnovialabs editorial team
In short
VS Code debugging is configured in .vscode/launch.json: a launch configuration starts your program under the debugger, an attach configuration connects to a process that is already running; then you set breakpoints, conditional if needed, and inspect variables, the call stack and watch expressions.
How it works
The VS Code debugger pauses your program on a chosen line and shows its state: variable values, the call stack, the result of expressions. That is faster and more reliable than sprinkling console.log or print and rerunning the code.
Everything is configured in .vscode/launch.json. It holds a list of configurations, each describing one way to run:
"request": "launch"— VS Code starts the program under the debugger;"request": "attach"— VS Code connects to a process that is already running.
The easiest way to create the file is the Run and Debug panel: "create a launch.json file", then choose an environment. Start with F5; toggle a breakpoint with F9 or by clicking left of a line number.
Node.js
A configuration for running a script:
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Run server",
"program": "${workspaceFolder}/src/index.js",
"skipFiles": ["<node_internals>/**"],
"env": { "NODE_ENV": "development" }
}
]
}
skipFileskeeps the debugger from stepping into Node.js internals.- TypeScript needs source maps: enable
sourceMapintsconfig.jsonand setoutFilesto the compiled.jspath in the configuration. - If the project runs through an npm script, the JavaScript Debug Terminal is handy: any command started in it attaches to the debugger automatically.
Browser
For frontend code, VS Code launches Chrome or Edge and lets you debug right in the editor:
{
"type": "chrome",
"request": "launch",
"name": "Debug frontend",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}"
}
Start the dev server first, then this configuration. webRoot must point to the folder the server serves sources from; otherwise breakpoints will not map to files. For Edge, change the type to msedge.
Python
You need Microsoft's Python extension. In current versions the debugger is called debugpy; older configurations may use the type python.
{
"type": "debugpy",
"request": "launch",
"name": "Python: current file",
"program": "${file}",
"console": "integratedTerminal"
}
For Django or FastAPI, use "module" (for example, uvicorn) with "args" instead of program. Make sure VS Code has the right virtual environment interpreter selected.
Conditional breakpoints and logpoints
Right-click left of a line number to see the options:
- Conditional Breakpoint → Expression — pause only when the expression is true, such as
user.id === 42orlen(items) > 100. Indispensable inside loops. - Hit Count — pause on the Nth pass through the line.
- Logpoint — instead of pausing, print a message to the Debug Console. Expressions go in curly braces:
Order {order.id}, total {order.total}. No code changes, nothing ends up in a commit.
Logpoints shine when pausing would change behavior: timeouts, network requests, animations.
Watch, call stack and Debug Console
While the program is paused, the Run and Debug panel shows:
- Variables — local and global variables in the current scope;
- Watch — expressions re-evaluated on every step:
cart.items.length,response.status; - Call Stack — the chain of calls; clicking a frame shows its variables;
- Debug Console — evaluate any expression in the context of the current pause point.
Stepping: F10 steps over, F11 steps into, Shift+F11 steps out, F5 continues.
Attaching to a running process
Sometimes VS Code is not the one starting the program: Docker, another terminal, a process manager. That is when you attach.
Node.js: start the process with --inspect (or --inspect-brk to wait for the debugger on the first line):
{
"type": "node",
"request": "attach",
"name": "Attach to Node",
"port": 9229
}
You can also use "processId": "${command:PickProcess}" and pick the process from a list.
Python: run python -m debugpy --listen 5678 --wait-for-client app.py and connect with a configuration that has "request": "attach" and "connect": { "host": "localhost", "port": 5678 }.
For a process inside a container, forward the debugger port and set localRoot/remoteRoot (Node.js) or pathMappings (Python) so container paths match project files. Never expose a debugger port publicly on production servers: it allows arbitrary code execution.
Common problems
- Grey "unbound" breakpoint — the debugger could not map the file: check source maps,
webRoot,outFilesor container paths. - The program exits before the breakpoint — use
--inspect-brkor--wait-for-client. - Python cannot find packages — the wrong interpreter is selected.