Debug an n8n Custom Node in VS Code with Breakpoints
In this video
Here's how most people debug an n8n node: add a console.log, run it, look for the line in a terminal full of n8n's own output, then add another console.log.
There's a better way. Put a breakpoint on a line, run the node in n8n, and your editor stops there and shows you everything: the incoming items, the parameters, and exactly what the API sent back.
If you made your project with n8n's own starter, npm create @n8n/node, it's already set up for this. It's just easy to miss. This guide assumes that kind of project, as built in Part 2 of the series.
What npm run dev runs
It helps to know what npm run dev is doing. It runs two things at once:
- TypeScript, watching your files and recompiling when you save.
- A whole n8n, on your machine, in its own folder, separate from any other n8n you have. Your project is linked into that n8n's custom nodes folder, linked rather than copied, so every rebuild shows up straight away.
Two things follow from that. It starts the latest n8n each time, so a new n8n release can change things under you. And because it has its own folder, workflows and credentials you make there don't appear in your other n8n.
The file you never opened
Open your project's .vscode folder. There's a launch.json that came with the starter, with one entry:
{
"name": "Attach to running n8n",
"request": "attach",
"processId": "${command:PickProcess}",
"type": "node"
}
That's the whole trick. Your node doesn't run on its own; it runs inside n8n. So you don't start a debugger, you attach one to the n8n that's already running.
Now open tsconfig.json: "sourceMap": true. That's what lets the debugger show your TypeScript rather than the compiled JavaScript. That's set up for you too.
Attach and stop on a line
- Run
npm run dev, as always. - In VS Code, open Run and Debug, pick Attach to running n8n, and press play.
- It asks which process. Pick the one that's n8n, not TypeScript's watcher. The status bar changes colour: you're attached.
- Click in the margin next to a line in your
execute()function. A red dot appears. - In n8n, press Execute step on your node.
VS Code stops on that line.
If attaching doesn't work on your machine, run n8n yourself with Node's --inspect flag and attach to that instead. n8n-node dev --external-n8n points the tool at an n8n you started, as long as it runs with N8N_DEV_RELOAD=true.
What you can see while paused
Now everything is visible:
- the items coming into your node
- what
getNodeParameterreally returned, which is often not what you expected - the response from your API, exactly as it came back, before your code touched it
You can also type into the Debug Console while paused. Ask for items[0].json.email and it answers. That's usually the moment you find the bug: something is undefined that you were sure was there.
Then step over, line by line, and watch the values change.
Where it won't stop
A few places this won't help, so you don't lose an evening to them.
Declarative requests. In a declarative node, most requests are described, not written. A breakpoint needs a function of yours to stop in. It works in execute(), in the methods that fill your dropdowns and list searches, and in any small function you hook into a declarative request, but not in the description of a request.
Description changes. Changes to the node's description, its fields and labels, don't show up on a rebuild. n8n's docs say to stop npm run dev and start it again. If a new field isn't appearing, that's almost always why.
A heads-up for n8n 3.0. n8n's docs say that from n8n 3.0, n8n-node dev starts n8n in a container (Docker or Podman). Attaching to a process inside a container needs a different setup, so if you're on 3.0, use --external-n8n with an n8n you run yourself and attach to that. At the time of writing, the current n8n release is 2.x, where everything above works as described.
Moving your node to 3.0? The n8n 3.0 upgrade checklist for community node makers covers the container dev setup, the new test data volumes and the other changes.
Three things that look like bugs
Your node isn't in the nodes panel. Search for the node's name, "Acme CRM", not the package name, n8n-nodes-acme-crm. They're often different.
Credentials of type "acmeCrmApi" aren't known. The name in your credential class and the name your node asks for in its credentials list don't match. Capital letters count. Part 4 covers this.
A blank icon. The icon property needs file: in front of the file name, and the file has to sit next to the node:
icon: 'file:acme.svg',
Recap
Next time something's wrong, skip the console.log. Run npm run dev, attach with the launch configuration that came with your project, and put a breakpoint where you think it breaks. And if the panel doesn't change, restart.
If you're starting a node from scratch, the Build your own n8n node series goes from an empty folder to a verified node.
One more thing. NativeShip, the tool we build, generates the node from your OpenAPI spec and checks it with a build (compile, n8n's linter and request validation) before you ever get to this point.
Frequently asked questions
Run npm run dev, then in VS Code open Run and Debug, choose "Attach to running n8n" (the launch configuration the n8n-node starter includes), and pick the n8n process. Set a breakpoint in your code and run the node in n8n.
A declarative node's requests are described, not written as functions, so there's no line of yours to stop on. Breakpoints work in execute(), in loadOptions and list search methods, and in any function you hook into a declarative request.
Changes to a node's description, such as fields and labels, aren't picked up on a rebuild. Stop npm run dev and start it again.
The icon property needs the file: prefix and the extension, like file:acme.svg, and the file has to sit in the same folder as the node. Use an SVG with a square canvas, or a 60×60 PNG.