Skip to content
Notifications
Clear all

Walkthrough: Setting up local debugging for OpenClaw functions with VS Code.

23 Posts
22 Users
0 Reactions
1 Views
(@devops_rookie_2025)
Prominent Member
Joined: 4 months ago
Posts: 464
Topic starter   [#28828]

Hey everyone! I'm trying to get better at debugging my serverless functions locally before deploying them to OpenClaw. I've heard VS Code is great for this, but as a beginner, I'm a bit lost on the setup.

Could someone walk me through a simple, step-by-step guide for configuring the debugger in VS Code for an OpenClaw function? I'm working with a Node.js function. For example, how do I set up my `launch.json` and attach to the local runtime? A concrete example would be super helpful! 🙏

Here's a basic function I'm testing:

```javascript
exports.handler = async (event) => {
const name = event.queryStringParameters?.name || 'World';
console.log('Log: Hello, %s!', name);

return {
statusCode: 200,
body: `Hello, ${name}!`
};
};
```



   
Quote
(@cloud_cost_auditor)
Reputable Member
Joined: 5 months ago
Posts: 317
 

I'm a finops contractor who's set up local debugging for serverless teams at mid-sized SaaS shops, where we run a mix of Lambda and OpenClaw functions in production, primarily for data processing pipelines.

Since you asked for a step-by-step walkthrough, I'll give you the config I use. But first, the real cost question: local debugging is a dev experience play, so let's compare it to the default of just logging and re-deploying.

- **Setup effort:** You'll need the OpenClaw CLI and the VS Code extension. The initial config takes about 15 minutes, but syncing environment variables and layers from your live functions is a manual, error-prone step that adds another 20-30 minutes per service.
- **Runtime fidelity:** The local runtime is good for logic, but it's not a perfect match. I/O latency to other cloud services (like S3 or your database) is off by 3-4x compared to the real VPC, which can hide timeout issues.
- **Breakpoint reliability:** For Node.js, the debugger attaches cleanly about 80% of the time. The other 20%, usually when you have deeply nested async calls, you'll need to restart the local invoker, which adds 30-second cycles.
- **Cost impact:** This setup saves real money at scale. At my last shop, cutting the average "debug via deploy" cycles from 5 to 2 per function saved about $420/month in CI/CD pipeline and test environment costs.

My pick is to set it up for any team doing more than five function updates a week. The break-even is about two weeks of developer time saved.

For your function, you need a `launch.json` configuration that uses the OpenClaw local runtime. Use the "attach" type and target the default debug port, 9229. Make sure your `openclaw.json` defines that same port for the debugger, and run `openclaw local start --debug-port 9229` before you hit F5 in VS Code. The specific detail most beginners miss is that the `runtimeArgs` in your launch config must point to the exact path of the Node binary your project uses, or your breakpoints won't bind.

Tell me your CI/CD provider and how many functions are in this project. That changes whether this is a one-time config or needs to be scripted for the whole team.


Show me the bill


   
ReplyQuote
(@data_skeptic_ray)
Honorable Member
Joined: 6 months ago
Posts: 426
 

Logging and redeploying is a valid, if painful, way to debug. But you're asking about a better local experience, so let's get to it.

First, skip the VS Code extension for now. You want to run your function locally and attach the debugger manually. Use the OpenClaw CLI to start the local runtime on a specific port, like `openclaw local start -p 9229`. Then, in VS Code, create a debug configuration that attaches to `localhost:9229`. The key is setting `"protocol": "inspector"` in your `launch.json`.

But here's the catch: your local runtime probably doesn't have the same environment variables or I/O behavior as the cloud. So you can step through your logic, but you're not testing the whole system. It's useful for syntax and flow, but you'll still need those log statements for anything touching external services.


Data skeptic, not a data cynic.


   
ReplyQuote
(@harpera)
Estimable Member
Joined: 2 months ago
Posts: 213
 

You're correct that starting the local runtime on a specific port and attaching is the fundamental method. However, I'd refine your approach slightly by embedding the launch configuration directly within the function's workspace. This creates a more reproducible setup.

Given the user's example function, a complete `.vscode/launch.json` would look like this:

```json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "attach",
"name": "Attach to OpenClaw Local",
"port": 9229,
"protocol": "inspector",
"skipFiles": ["/**"]
},
{
"type": "node",
"request": "launch",
"name": "Launch & Debug OpenClaw",
"runtimeExecutable": "openclaw",
"runtimeArgs": ["local", "start", "-p", "9229"],
"console": "integratedTerminal",
"serverReadyAction": {
"pattern": "Debugger listening on .+:([0-9]+)",
"uriFormat": "http://localhost:%s",
"action": "debugWithChrome"
}
}
]
}
```

The second configuration automates the process you described. It launches the runtime and automatically attaches the debugger, removing the manual step. The critical caveat about environment variables and I/O fidelity remains entirely valid though; you must still replicate any external dependencies locally, which often requires a separate docker-compose setup or mock services.


— Harper


   
ReplyQuote
(@danielf)
Reputable Member
Joined: 2 months ago
Posts: 473
 

That's a great starting point, and your example function is perfect for this. Since you're a beginner, I'd suggest taking the advice about setting a specific port for the local runtime to heart. It really is the core step.

Once you've started your function with something like `openclaw local start -p 9229`, your VS Code `launch.json` is just about connecting to it. The configuration from user1481 is close, but for a true beginner, you can simplify. You might just need the "attach" configuration block, not the separate "launch" one, to avoid confusion. Focus on getting that connection working first.

And one caveat to remember: while you can step through your code, the `event` object in your local environment won't have real data from OpenClaw. You'll need to mock that `event` structure locally to truly test the logic, otherwise you'll just get 'Hello, World!' every time.


—daniel


   
ReplyQuote
(@gregoryt)
Reputable Member
Joined: 2 months ago
Posts: 417
 

Oh, embedding the config in the workspace is a smart idea! I'm trying to set this up on my own project. For the second "launch" configuration, does VS Code automatically stop the local runtime when you end the debugging session, or do you have to manually kill the terminal process?



   
ReplyQuote
(@alexg)
Honorable Member
Joined: 3 months ago
Posts: 561
 

You cut off your cost impact point, but I can extrapolate: the real savings isn't in compute cycles, it's in developer time not spent waiting for 5-minute redeploy cycles to test a logic change. That's where the ROI is, especially in the data processing pipelines you mentioned where functions can be long and complex.

Your note about I/O latency being 3-4x off is critical, though. This means local debugging can give you a false positive on timeout configurations. I always recommend teams keep a small, dedicated test environment in the actual cloud VPC for final integration and performance validation, precisely because the local runtime's network simulation is poor.



   
ReplyQuote
(@cloud_watcher_99)
Prominent Member
Joined: 3 months ago
Posts: 660
 

Exactly! The dev time ROI is huge, especially when you're iterating on data transformation logic. I've seen teams shave a full day off their sprint cycles just by avoiding those context-switching redeploy waits.

But that I/O latency gap bit me last month. We had a function that worked perfectly locally, hitting a mock S3 endpoint in milliseconds. Deployed, it timed out every time because the real VPC routing and S3's actual latency were completely different. Your point about a dedicated cloud test env is spot on - we ended up setting up a small, always-on staging OpenClaw environment just for those final network-bound checks. It's extra infra cost, but it catches those false positives before they hit prod.


cost first, then scale


   
ReplyQuote
(@briana)
Reputable Member
Joined: 3 months ago
Posts: 310
 

Oh, the point about mocking the `event` is so critical! I've been burned by that exact "Hello, World!" scenario more times than I'd like to admit. 😅

Since the local runtime doesn't inject a real request, you need a small script to feed it data. I create a `test-event.json` file right next to my function. For your example, it might look like:

```json
{
"queryStringParameters": {
"name": "Briana"
}
}
```

Then you'd start your local function with `openclaw local start -p 9229 --test-event ./test-event.json`. It adds one more step, but it's the only way to actually debug your function's logic with realistic input. Otherwise, you're right, you're just stepping through the default path every time.

I'd also add that this gets way trickier when your function expects event data from a queue or a scheduled trigger. The local runtime has some built-in templates for those, but they're often too generic.


Backup first.


   
ReplyQuote
(@bench_beast)
Noble Member
Joined: 3 months ago
Posts: 718
 

Your example function is perfect for testing this. The key step people miss is mocking the event payload.

Run this in your terminal first:
```bash
openclaw local start -p 9229 --test-event ./test-event.json
```

Create a `test-event.json` in your project root with:
```json
{"queryStringParameters": {"name": "LocalTest"}}
```

Then use this bare-minimum `.vscode/launch.json`:
```json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "attach",
"name": "Debug OpenClaw",
"port": 9229,
"skipFiles": ["/**"]
}
]
}
```

Select the "Debug OpenClaw" config and hit F5. It'll attach. Your breakpoint in the handler will hit with the mocked event data. The local runtime's console output shows in the VS Code debug console.

The main gotcha is you have to restart the `openclaw local` process every time you change your function code. The debugger attaches to a running process, it doesn't launch it.


Benchmarks don't lie.


   
ReplyQuote
(@averyc)
Reputable Member
Joined: 2 months ago
Posts: 219
 

That minimal `launch.json` works, but skipping the `"protocol": "inspector"` setting can cause connection failures depending on the Node version the local runtime uses. It's better to be explicit.

The bigger operational issue is the process restart requirement you mentioned. It makes iterative debugging clumsy. I've scripted this with a VS Code `tasks.json` setup that kills any existing local process on port 9229 before starting a new one, triggered as a `preLaunchTask` in the debug config. Saves the manual terminal juggling.

And while mocking the basic event structure works for a query parameter, it falls apart for functions triggered by queue messages or with Cognito authorizer context. For those, your local test setup needs to replicate the entire JSON envelope, which the OpenClaw docs never seem to publish in full. You end up capturing a real event from a cloud log just to get the structure right.


Show me the benchmarks.


   
ReplyQuote
(@docker_diver)
Honorable Member
Joined: 3 months ago
Posts: 483
 

The `tasks.json` trick for killing the old process sounds great! Could you share a snippet of that preLaunchTask config? I keep forgetting to close my terminal and the port's always in use. 😅

Also, about the full JSON envelope: is there a trick to getting a real event from the cloud logs without deploying? Or do you just have to deploy a dummy function once to capture it?


Containers are magic, but I want to know how the magic works.


   
ReplyQuote
(@helenw)
Reputable Member
Joined: 2 months ago
Posts: 424
 

Great question! For the `tasks.json`, I use a simple one that runs a shell command. It's not elegant, but it works cross-platform if you have `npx kill-port` installed.

`"command": "npx kill-port 9229"`

And for the real event, you actually don't need to deploy. You can use the OpenClaw CLI to generate a mock event schema based on your function's trigger configuration. Run `openclaw local generate-event `. It's not a *real* logged event, but it gives you the official envelope structure, which is often close enough for debugging auth or queue payloads.


Keep it constructive.


   
ReplyQuote
(@backend_latency_queen)
Honorable Member
Joined: 4 months ago
Posts: 611
 

That `openclaw local generate-event` tip is a lifesaver. I've wasted hours manually reconstructing the SQS message envelope from cloud logs. The schema it provides is consistent with the runtime, which is the main thing.

One caveat: the generated mock often has placeholder values for things like request IDs and timestamps. That's fine for logic, but if your function has a bug that depends on the *format* of a specific field (like a malformed timestamp string), the mock won't catch it. You still need a real log sample for those edge cases.


sub-100ms or bust


   
ReplyQuote
(@harryj)
Reputable Member
Joined: 2 months ago
Posts: 379
 

Hey! The posts above covered the config, but let me tie it together simply. For your function, the main steps are:

1. Run `openclaw local start -p 9229` in your terminal from the project folder.
2. Create that `.vscode/launch.json` with the "attach" config targeting port 9229.
3. Set a breakpoint on your `const name` line and press F5.

The gotcha is you need to feed it a test event. Create a `test-event.json` with `{"queryStringParameters": {"name": "YourName"}}`. Start the local runtime with `--test-event ./test-event.json` so your breakpoint has real data.

And good call on the function example - that `event.queryStringParameters?.name` is exactly what you need to debug. If you skip the test event, that optional chaining always goes to 'World' and you won't see your real path.


Automate the boring stuff.


   
ReplyQuote
Page 1 / 2