Debug in VS Code
Goal: set a breakpoint in a .bynk file, press Debug, and step through
your code as it runs — under Node (tests) or workerd (the dev server).
The vscode-bynk extension delegates to VS Code’s built-in JavaScript debugger:
it compiles your project, starts the V8 inspector, and attaches automatically.
Breakpoints set in .bynk sources resolve through the emitted source maps to the
exact statement, so you debug Bynk, not the generated TypeScript.
Prerequisites
Section titled “Prerequisites”- The
vscode-bynkextension installed (see Set up editor support). bynkcandbynkon yourPATH(or setbynk.compilerPath/bynk.bynkPath).- Node ≥ 22.6 for the test path (the emitted
.tsruns under Node’s type-stripping). For the dev-server path, the same toolchainbynk devneeds (wrangler).
Debug a test
Section titled “Debug a test”- Open a
.bynktest file and click in the gutter to set a breakpoint — on a test-body statement, or in the production code the test exercises. - Run or debug it from the editor: each test shows a
▷ Run Test | Debug TestCodeLens (and the native run/debug glyphs in the gutter) — click Debug Test. You can also use the Testing view’s Debug action or profile. - The extension shells
bynkc test --inspect, attaches, and execution pauses at your breakpoint. Step, inspect variables, and continue as usual.
The whole suite runs under the debugger; your breakpoint decides where it stops. The Bynk runtime and generated glue are skip-stepped, so stepping stays inside your code.
Debug the dev server
Section titled “Debug the dev server”-
Set a breakpoint in a
.bynkhandler (e.g. aservice/agentrequest handler). -
Create a
launch.json(Run → Add Configuration → Bynk) — or use the snippet Bynk: Debug dev server:{"type": "bynk","request": "launch","name": "Bynk: Debug dev server","mode": "dev"} -
Press F5. The extension runs
bynk dev --inspect, attaches towrangler’s inspector, and your handler pauses on a real request — send one (e.g.curl http://127.0.0.1:8787/) and step through it.
For a multi-context project, set "context": "<name>" to choose which worker to
serve and attach to. "port" overrides the inspector port (default 9229).
Values read in Bynk
Section titled “Values read in Bynk”When you inspect a value, the debugger shows it in Bynk’s vocabulary rather than its emitted shape — under both the test (Node) and dev-server (workerd) paths:
| You wrote | The debugger shows |
|---|---|
Ok(42) | Ok(42) (not {tag: "Ok", value: 42}) |
Some("hi") / None | Some("hi") / None |
| a sum variant | BadRequest("…") / NotFound |
| nested | Ok(Some(42)) |
This applies to hovers, the Variables pane, and Watch. Turn it off with
bynk.debug.semanticValues: false to see the raw emitted shape.
A note on deeply-nested values. On the dev-server (workerd) path the rendering is reconstructed from the debugger’s value preview, which elides deep nesting — a nested value may show one level (
Ok(…)) with the inner value one expand away (it renders in Bynk vocabulary too when you open it). The test path renders nesting fully inline.
The handler frame in Bynk
Section titled “The handler frame in Bynk”Stopped inside a handler, the Variables pane groups the frame in Bynk’s terms
rather than as a flat list of emitted locals: the capabilities a handler consumes
appear together under Capabilities, and an agent’s state under State — both
sorted to the top, still expandable, with your own bindings and request parameters
listed below. Same toggle (bynk.debug.semanticValues).
The Call Stack reads in Bynk too: a handler frame is named for its Bynk
operation — GET "/", bump(amount) — rather than the emitted function (http_GET),
while toolchain and runtime frames stay greyed out (skip-stepped). Clicking a frame
still navigates to its .bynk line.
The compiler temporaries the lowering spills (__-prefixed bindings from ?/match)
are filtered out, so stepping shows your bindings rather than the lowering’s. Turn the
whole thing off (bynk.debug.semanticValues: false) to see them and the raw shapes.
Surfacing the
byactor as part of the frame is a planned addition.
How it works
Section titled “How it works”Per ADR 0104 (the debug-launch model) this is glue, not a Debug Adapter: the
extension contributes a bynk debug type whose
configuration provider starts the inspector by shelling the
--inspect CLIs and hands off to VS Code’s
JavaScript debugger (pwa-node). The source maps — .bynk files referenced by
absolute path so an editor breakpoint resolves to the same source the debugger
loads — do the breakpoint relocation. You can also run the CLIs directly and
attach any JavaScript debugger by hand; the one-click flow just automates that.
Troubleshooting
Section titled “Troubleshooting”- The breakpoint shows as unbound (hollow). Make sure the project is the open workspace folder — the debugger anchors source resolution there. Rebuild if the emitted output is stale.
nodenot found / wrong version. The test path needs Node ≥ 22.6 for TypeScript type-stripping; check withbynk doctor.- Nothing happens on the dev path. Confirm
wrangleris available (bynk doctor --only deploy) and that a request actually reaches the handler.