Skip to content
Innovialabs

Debugging in VS Code: Breakpoints and launch.json

Practice4 min

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" }
    }
  ]
}
  • skipFiles keeps the debugger from stepping into Node.js internals.
  • TypeScript needs source maps: enable sourceMap in tsconfig.json and set outFiles to the compiled .js path 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 === 42 or len(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, outFiles or container paths.
  • The program exits before the breakpoint — use --inspect-brk or --wait-for-client.
  • Python cannot find packages — the wrong interpreter is selected.

FAQ

Usually yes, if the configurations are shared by the team: new developers will not have to set up debugging from scratch. Keep personal settings and secrets out of it; load environment variables from .env via envFile.

We can help with this

If you'd rather get the result than figure it all out yourself — this is what we do every day.

Discuss a project

Read next

Have an idea or a task? Tell us about it

We'll reply within one business day and propose a solution. The consultation is free.