#VITE SERVER FIX .
1 messages · Page 1 of 1 (latest)
This is usually not fixed by pnpm ui:dev alone. ui:dev only serves the Control UI; the media/avatar checks still come from the running Gateway, and it only serves local files from allowed media/workspace roots.
Try this from the OpenClaw repo:
pnpm install
pnpm openclaw setup # first run only
pnpm gateway:watch
pnpm ui:dev
Also: the script in current source is pnpm ui:build, not pnpm build:ui.
That Outside allowed folders line means the UI is trying to preview a local file path that the Gateway refuses to read. If it’s your agent avatar/DP, put the image under the agent workspace or OpenClaw media dir, then set it with a workspace-relative path, e.g.:
mkdir -p ~/.openclaw/workspace/avatars
# copy your image there, then:
openclaw agents set-identity --agent main --avatar avatars/openclaw.png
Then restart/refresh the gateway/UI.
Can you paste openclaw --version and openclaw status --all if it still fails? Version mismatch is a super common cause here. Docs: https://docs.openclaw.ai/help/faq and dev setup: https://docs.openclaw.ai/start/setup
ayu@MSI:~/openclaw-main$ pnpm openclaw setup
openclaw@2026.5.8 openclaw /home/ayu/openclaw-main
node scripts/run-node.mjs setup
🦞 OpenClaw 2026.5.8 (3f0c0ce) — One CLI to rule them all, and one more restart because you changed the port.
Config OK: ~/.openclaw/openclaw.json
Error: EACCES: permission denied, open '/home/ayu/.openclaw/workspace/AGENTS.md'
 ELIFECYCLE  Command failed with exit code 1.
ayu@MSI:~/openclaw-main$
after pnpm install
That’s a permissions/ownership issue, not a pnpm install issue. Something in ~/.openclaw/workspace is not writable by your user — most commonly because OpenClaw was run once with sudo.
Check it:
ls -ld ~/.openclaw ~/.openclaw/workspace
ls -l ~/.openclaw/workspace/AGENTS.md
If you see root root or another owner, fix ownership back to your user:
sudo chown -R "$USER:$USER" ~/.openclaw
Then run again without sudo:
pnpm openclaw setup
pnpm gateway:watch
pnpm ui:dev
Important: don’t run pnpm openclaw setup / pnpm gateway:watch with sudo unless docs explicitly say so, or it’ll keep creating root-owned files in your state dir.
ayu@MSI:~/openclaw-main$ ls -ld ~/.openclaw ~/.openclaw/workspace
drwx------ 31 ayu ayu 4096 May 9 16:46 /home/ayu/.openclaw
drwxr-xr-x 2 root root 4096 May 3 10:25 /home/ayu/.openclaw/workspace
ayu@MSI:~/openclaw-main$ ls -l ~/.openclaw/workspace/AGENTS.md
ls: cannot access '/home/ayu/.openclaw/workspace/AGENTS.md': No such file or directory
ayu@MSI:~/openclaw-main$
Yep, found it: ~/.openclaw/workspace is owned by root root, so your normal ayu user can’t create AGENTS.md there.
Run:
sudo chown -R ayu:ayu ~/.openclaw/workspace
Or, to fix any other root-owned files under OpenClaw too:
sudo chown -R ayu:ayu ~/.openclaw
Then verify:
ls -ld ~/.openclaw/workspace
It should show ayu ayu, then rerun:
pnpm openclaw setup
pnpm gateway:watch
pnpm ui:dev
And avoid running OpenClaw commands with sudo after this, or it’ll recreate the same problem.
still
voice-1778346117641---8902b1ad-2926-4b2a-87dd-5d4c8e0ce2d3.mp3
Unavailable
Outside allowed folders
Okay, that means the workspace ownership fix solved setup, but this is a separate media-path issue.
Two likely causes:
- You’re looking at an old transcript item. If that voice file was saved with a bad path, fixing ownership won’t rewrite the old message.
- The running Gateway is still generating/recording TTS files outside its allowed media roots.
Please run these and paste the output:
openclaw --version
openclaw gateway status
ls -ld /tmp/openclaw /tmp/openclaw-$(id -u) 2>/dev/null || true
find ~/.openclaw /tmp/openclaw /tmp/openclaw-$(id -u) ~/openclaw-main -name 'voice-1778346117641---8902b1ad-2926-4b2a-87dd-5d4c8e0ce2d3.mp3' -print 2>/dev/null
Also restart the dev gateway after the ownership fix:
pnpm gateway:watch
Then hard-refresh the UI and create a new voice/TTS response. If only the old voice-1778...mp3 card is broken, that’s just stale session media. If a brand-new one is still broken, the find output will tell us exactly which folder OpenClaw is writing to outside the allowlist.
ayu@MSI:~$ openclaw --version
OpenClaw 2026.5.8 (3f0c0ce)
^Cayu@MSI:~openclaw gateway statusus
🦞 OpenClaw 2026.5.8 (3f0c0ce) — I'm the reason your shell history looks like a hacker-movie montage.
│
â—‡
Service: systemd user (enabled)
File logs: /tmp/openclaw/openclaw-2026-05-09.log
Command: /home/ayu/.nvm/versions/node/v22.22.2/bin/node /home/ayu/openclaw-main/dist/index.js gateway --port 18789
Service file: ~/.config/systemd/user/openclaw-gateway.service
Service env: OPENCLAW_GATEWAY_PORT=18789
Service config looks out of date or non-standard.
Service config issue: Gateway service PATH includes version managers or package managers; recommend a minimal PATH. (/home/ayu/.nvm/versions/node/v22.22.2/bin)
Service config issue: Gateway service uses Node from a version manager; it can break after upgrades. (/home/ayu/.nvm/versions/node/v22.22.2/bin/node)
Service config issue: System Node 22 LTS (22.16+) or Node 24 not found; install it before migrating away from version managers.
Recommendation: run "openclaw doctor" (or "openclaw doctor --repair").
Config (cli): ~/.openclaw/openclaw.json
Config (service): ~/.openclaw/openclaw.json
Gateway: bind=loopback (127.0.0.1), port=18789 (service args)
Probe target: ws://127.0.0.1:18789
Dashboard: http://127.0.0.1:18789/
Probe note: Loopback-only gateway; only local clients can connect.
CLI version: 2026.5.8 (~/openclaw-main/openclaw.mjs)
Gateway version: 2026.5.8
Runtime: stopped (state inactive, sub dead, last exit 0, reason 1)
Connectivity probe: ok
Capability: admin-capable
Service is loaded but not running (likely exited immediately).
File logs: /tmp/openclaw/openclaw-2026-05-09.log
Logs: journalctl --user -u openclaw-gateway.service -n 200 --no-pager
Restart attempts: /home/ayu/.openclaw/logs/gateway-restart.log
Listening: 127.0.0.1:18789
Troubles: run openclaw status
Troubleshooting: https://docs.openclaw.ai/troubleshooting
ayu@MSI:~$
Good, version matches. The interesting bit is this:
Runtime: stopped
Connectivity probe: ok
Listening: 127.0.0.1:18789
So your systemd Gateway service is stopped, but something else is still listening on port 18789 — likely an old/manual pnpm gateway:watch or node dist/index.js gateway process. That can absolutely make the UI show stale/broken media behavior.
First, identify what is actually serving the UI/API:
ss -ltnp | grep 18789 || true
ps aux | grep -E 'openclaw|dist/index|gateway' | grep -v grep
Then pick one mode:
Dev/source mode recommended here:
openclaw gateway stop # stops the systemd service if it is enabled/running
cd ~/openclaw-main
pnpm gateway:watch
pnpm ui:dev
Don’t use the systemd service at the same time as pnpm gateway:watch.
Also since the service command points at dist/index.js, if you want to use the installed service instead, you need to build the backend too — pnpm ui:build only builds the UI:
cd ~/openclaw-main
pnpm build
openclaw gateway restart
For the voice-...mp3 card specifically: if that card is from an older session, it may just be stale/bad transcript media. After restarting the actual gateway process, create a new response and check whether the new audio card still says Outside allowed folders.
ayu@MSI:~$ ss -ltnp | grep 18789 || true
LISTEN 0 511 127.0.0.1:18789 0.0.0.0:* users:(("openclaw",pid=148495,fd=28))
LISTEN 0 511 [::1]:18789 [::]:* users:(("openclaw",pid=148495,fd=29))
ayu@MSI:~$ ps aux | grep -E 'openclaw|dist/index|gateway' | grep -v grep
ayu 148388 0.0 0.7 1350076 95372 pts/3 Sl+ 17:00 0:00 node /home/ayu/.nvm/versions/node/v22.22.2/bin/pnpm gateway:watch
ayu 148400 0.0 0.0 2804 1536 pts/3 S+ 17:00 0:00 sh -c node scripts/gateway-watch-tmux.mjs gateway --force
ayu 148401 0.0 0.3 882024 47736 pts/3 Sl+ 17:00 0:00 node scripts/gateway-watch-tmux.mjs gateway --force
ayu 148411 0.0 0.0 10316 4932 ? Ss 17:00 0:00 tmux new-session -d -s openclaw-gateway-watch-main -c /home/ayu/openclaw-main exec '/bin/bash' -lc 'cd '''/home/ayu/openclaw-main''' && exec '''env''' '''-u''' '''NO_COLOR''' '''OPENCLAW_GATEWAY_WATCH_TMUX_CHILD=1''' '''OPENCLAW_GATEWAY_WATCH_SESSION=openclaw-gateway-watch-main''' '''FORCE_COLOR=1''' '''/home/ayu/.nvm/versions/node/v22.22.2/bin/node''' '''scripts/watch-node.mjs''' '''gateway''' '''--force''''
ayu 148412 0.3 0.7 1335840 86472 pts/5 Ssl+ 17:00 0:01 /home/ayu/.nvm/versions/node/v22.22.2/bin/node scripts/watch-node.mjs gateway --force
ayu 148426 0.0 0.0 9280 4736 pts/3 S+ 17:00 0:00 tmux attach-session -t openclaw-gateway-watch-main
ayu 148428 0.0 0.6 1886004 74192 pts/5 Sl+ 17:00 0:00 /home/ayu/.nvm/versions/node/v22.22.2/bin/node scripts/run-node.mjs gateway --force
ayu 148495 4.7 3.1 23658108 387836 pts/5 Sl+ 17:00 0:18 openclaw
ayu 148725 0.7 3.8 26636192 465612 pts/0 Sl+ 17:01 0:02 node /home/ayu/openclaw-main/node_modules/.bin/vite
ayu@MSI:~$
Ahhh — now we found the real cause. You’re using pnpm ui:dev / Vite on port 5173.
In current source, the Vite dev server has a stubbed Control UI config:
assistantName: "",
assistantAvatar: "",
…and it does not include the Gateway’s localMediaPreviewRoots. So in Vite mode:
- agent DP/avatar can be blank
- local audio/media previews can say
Unavailable / Outside allowed folders
Your gateway process itself looks fine.
Use the Gateway-served UI instead if you want real agent identity + media previews:
cd ~/openclaw-main
pnpm ui:build
# gateway:watch is already running
Then open:
http://127.0.0.1:18789/
Not the Vite URL on 5173.
If you specifically need live Vite UI development, this is a dev-stub limitation/bug: ui/vite.config.ts would need its /__openclaw/control-ui-config.json stub to include localMediaPreviewRoots and a real assistantAvatar, or proxy that route to the gateway instead of stubbing it. For normal use, the gateway URL is the fix.
http://127.0.0.1:18789/ is good that vite ?
Yep — http://127.0.0.1:18789/ is the Gateway-served Control UI, not Vite.
That’s the right one for normal use because it gets real data from the Gateway: agent DP/avatar, media roots, auth, sessions, etc.
Vite is usually here instead:
http://127.0.0.1:5173/
Use Vite only when actively editing the UI frontend. For just using/testing OpenClaw, use 18789.
ayu@MSI:~/openclaw-main$ ps aux | grep -i openclaw | grep -v grep
ayu 148411 0.0 0.0 10448 5060 ? Ss 17:00 0:00 tmux new-session -d -s openclaw-gateway-watch-main -c /home/ayu/openclaw-main exec '/bin/bash' -lc 'cd '''/home/ayu/openclaw-main''' && exec '''env''' '''-u''' '''NO_COLOR''' '''OPENCLAW_GATEWAY_WATCH_TMUX_CHILD=1''' '''OPENCLAW_GATEWAY_WATCH_SESSION=openclaw-gateway-watch-main''' '''FORCE_COLOR=1''' '''/home/ayu/.nvm/versions/node/v22.22.2/bin/node''' '''scripts/watch-node.mjs''' '''gateway''' '''--force''''
ayu 148495 3.6 3.1 23655960 385768 pts/5 Sl+ 17:00 0:21 openclaw
ayu@MSI:~/openclaw-main$
That looks fine for dev/source mode. You have:
tmux ... openclaw-gateway-watch-main= the watcher wrapperopenclaw= the actual running Gateway on18789
So use:
http://127.0.0.1:18789/
If the UI there works, you don’t need pnpm ui:dev at all.
Only run Vite (5173) when editing frontend code. If you do run Vite, the missing DP / Outside allowed folders issue is expected unless you patch/proxy the Vite bootstrap config.
ayu@MSI:~/openclaw-main$ openclaw gateway stop
🦞 OpenClaw 2026.5.8 (3f0c0ce) — Pairing codes exist because even bots believe in consent—and good security hygiene.
Stopped systemd service: openclaw-gateway.service
ayu@MSI:~/openclaw-main$ ps aux | grep -i openclaw | grep -v grep
ayu 148411 0.0 0.0 10860 5444 ? Ss 17:00 0:00 tmux new-session -d -s openclaw-gateway-watch-main -c /home/ayu/openclaw-main exec '/bin/bash' -lc 'cd '''/home/ayu/openclaw-main''' && exec '''env''' '''-u''' '''NO_COLOR''' '''OPENCLAW_GATEWAY_WATCH_TMUX_CHILD=1''' '''OPENCLAW_GATEWAY_WATCH_SESSION=openclaw-gateway-watch-main''' '''FORCE_COLOR=1''' '''/home/ayu/.nvm/versions/node/v22.22.2/bin/node''' '''scripts/watch-node.mjs''' '''gateway''' '''--force''''
ayu 148495 2.9 3.1 23654068 384652 pts/5 Sl+ 17:00 0:25 openclaw
ayu 153547 156 0.8 1040024 101836 pts/5 Rl+ 17:15 0:00 node /home/ayu/.openclaw/plugins-src/instagram-cli-4llm/dist/cli.js threads --limit 25
ayu@MSI:~/openclaw-main$
tmux ... openclaw-gateway-watch-main = the watcher wrapper
stp this
That’s expected: openclaw gateway stop only stops the systemd service. It does not stop the dev pnpm gateway:watch tmux session.
Right now your Gateway is still running from dev mode:
tmux ... openclaw-gateway-watch-main
openclaw
If you want to keep using http://127.0.0.1:18789/, leave it running.
If you want to stop the dev gateway too:
tmux kill-session -t openclaw-gateway-watch-main
Then verify:
ps aux | grep -i openclaw | grep -v grep
ss -ltnp | grep 18789 || true
Also this one is separate from the gateway:
node ~/.openclaw/plugins-src/instagram-cli-4llm/dist/cli.js threads --limit 25
That looks like an Instagram plugin/tool process. If it stays stuck or eats CPU, you can stop it with:
kill 153547
So: systemd is stopped âś…, dev gateway is still running via tmux âś….
Yep — stop the dev watcher with:
tmux kill-session -t openclaw-gateway-watch-main
Then confirm it’s gone:
ps aux | grep -i openclaw | grep -v grep
ss -ltnp | grep 18789 || true
After that, http://127.0.0.1:18789/ will stop working until you start the gateway again with either:
pnpm gateway:watch
or the systemd service:
openclaw gateway start
May 09 17:48:32 MSI node[29327]: 2026-05-09T17:48:32.709+00:00 [ws] protocol mismatch conn=5cf21562-5a35-4f7a-ae51-c383b59da3c0 remote=127.0.0.1 client=openclaw-control-ui webchat v2026.5.6
May 09 17:48:32 MSI node[29327]: 2026-05-09T17:48:32.711+00:00 [ws] closed before connect conn=5cf21562-5a35-4f7a-ae51-c383b59da3c0 peer=127.0.0.1:51638->127.0.0.1:18789 remote=127.0.0.1 fwd=n/a origin=http://127.0.0.1:18789 host=127.0.0.1:18789 ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36 code=1002 reason=protocol mismatch
May 09 17:48:48 MSI node[29327]: 2026-05-09T17:48:48.716+00:00 [ws] protocol mismatch conn=2036bc1a-a863-45be-bb5b-1c352d51ecf1 remote=127.0.0.1 client=openclaw-control-ui webchat v2026.5.6
May 09 17:48:48 MSI node[29327]: 2026-05-09T17:48:48.720+00:00 [ws] closed before connect conn=2036bc1a-a863-45be-bb5b-1c352d51ecf1 peer=127.0.0.1:56182->127.0.0.1:18789 remote=127.0.0.1 fwd=n/a origin=http://127.0.0.1:18789 host=127.0.0.1:18789 ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36 code=1002 reason=protocol mismatch
That log means your browser UI is older than the Gateway:
client=openclaw-control-ui webchat v2026.5.6
Gateway version: 2026.5.8
reason=protocol mismatch
So the Gateway is 2026.5.8, but the page loaded 2026.5.6 UI assets. Fix is to rebuild/refresh the Control UI assets and clear the stale browser cache.
Run:
cd ~/openclaw-main
pnpm ui:build
openclaw gateway restart
Then in Chrome open http://127.0.0.1:18789/ and hard refresh:
Ctrl+Shift+R
If it still says protocol mismatch, clear site data for 127.0.0.1:18789:
DevTools → Application → Storage → Clear site data
Then reload.
Also make sure you’re not opening the Vite URL (5173) unless you’re actively developing the UI. For normal use, use only:
http://127.0.0.1:18789/
okay its fixed ,, now tell me what is Tailscale?
Nice, glad it’s fixed 🎉
Tailscale is basically a private VPN for your devices, but much easier than traditional VPN setup.
It lets your laptop, phone, VPS, home server, etc. talk to each other as if they’re on the same private network — even if they’re behind NAT, home routers, mobile data, hotel Wi‑Fi, etc.
For OpenClaw, people use Tailscale to do things like:
- access their home/PC Gateway from another device
- connect a VPS to their local machine securely
- avoid exposing OpenClaw directly to the public internet
- use private IPs like
100.x.y.zbetween trusted devices
Example idea:
Your laptop runs OpenClaw Gateway on 18789
Your phone/VPS connects over Tailscale
No public port forwarding needed
So instead of opening http://your-public-ip:18789, you’d use the Tailscale private address, like:
http://100.x.y.z:18789
It’s very useful, but you still need OpenClaw auth/token/password configured properly — Tailscale protects the network path, not the app itself.
i have openclaw running on wsl on laptop is there any way to run it 24/7 there?
Yes, but with an important caveat: WSL can run OpenClaw for a long time, but a laptop is only “24/7” if Windows stays awake. If Windows sleeps, reboots, or WSL shuts down, OpenClaw stops.
Best options:
Option A — okay for laptop/dev use
Keep Windows awake + run OpenClaw inside WSL:
cd ~/openclaw-main
pnpm gateway:watch
Then prevent sleep in Windows:
Settings → System → Power & battery → Screen and sleep → set sleep to Never while plugged in
You can also use Microsoft PowerToys → Awake.
Option B — auto-start when Windows/WSL starts
Use Windows Task Scheduler to run something like:
wsl -d Ubuntu -- bash -lc "cd ~/openclaw-main && pnpm gateway:watch"
That restarts it after login, but it still won’t survive laptop sleep/power-off.
Option C — actually reliable 24/7
Run OpenClaw on a VPS, mini PC, home server, or always-on machine. Then connect from your laptop/phone with Tailscale. This is what I’d recommend if you want it available all the time.
So: WSL is fine for development and “while my laptop is awake”; for real 24/7, use an always-on host.
thats great
Happy to help! 🦞
For your setup, I’d treat WSL as the dev box and later move to a VPS/Tailscale setup once you want it truly always-on. You’re on the right track.
It looks like the call failed because the voice-call plugin (using Twilio) requires a publicly reachable webhook URL, but it's currently set to a local address (http://127.0.0.1:3334).
To fix this, we need to configure a publicUrl in the plugin settings. If you have a tunnel (like Ngrok or Tailscale) or a static public IP/domain, let me know and I can update the configuration for you. Otherwise, I can't make the call until the system can receive callbacks from Twilio.
said him to initiate a call but it failed
Yep, that error makes sense. Twilio has to call back into your OpenClaw voice-call webhook, and 127.0.0.1:3334 only exists inside your laptop/WSL — Twilio can’t reach it.
You need one of these:
Best for quick testing: ngrok / Cloudflare Tunnel / public domain
Set plugins.entries.voice-call.config.publicUrl to the full public HTTPS webhook URL, usually ending with the voice webhook path, e.g.
plugins: {
entries: {
"voice-call": {
enabled: true,
config: {
provider: "twilio",
serve: { port: 3334, path: "/voice/webhook" },
publicUrl: "https://your-tunnel.example/voice/webhook"
}
}
}
}
Then restart Gateway and check:
openclaw voicecall setup
Tailscale note: normal Tailscale/Serve is private to your tailnet, so Twilio still cannot reach it. For Twilio you’d need Tailscale Funnel or another public HTTPS tunnel.
Docs: https://docs.openclaw.ai/plugins/voice-call and https://docs.openclaw.ai/cli/voicecall
So the call failed because the plugin config is local-only, not because your call command was wrong.
how to get a permanent public url ?
For Twilio voice-call, you need a stable public HTTPS URL. Good options:
Option 1 — Cloudflare Tunnel with your own domain ✅ recommended
You buy/use a domain, add it to Cloudflare, then create a named tunnel. You get a permanent URL like:
https://voice.yourdomain.com/voice/webhook
High-level flow:
cloudflared tunnel login
cloudflared tunnel create openclaw-voice
cloudflared tunnel route dns openclaw-voice voice.yourdomain.com
cloudflared tunnel run openclaw-voice
Then map it to your local voice-call server, usually http://127.0.0.1:3334.
Option 2 — ngrok reserved domain
Free ngrok URLs change. For permanent, you need a reserved/static domain from ngrok, then set:
https://your-static.ngrok.app/voice/webhook
Option 3 — VPS + reverse proxy
Run OpenClaw on a VPS, or proxy from VPS → your laptop. Most reliable for 24/7.
Option 4 — Tailscale Funnel
Normal Tailscale is private and Twilio cannot reach it. Tailscale Funnel can expose a public HTTPS URL, but I’d still prefer Cloudflare Tunnel or VPS for this.
After you have the URL, put the full webhook URL in config:
plugins: {
entries: {
"voice-call": {
enabled: true,
config: {
publicUrl: "https://voice.yourdomain.com/voice/webhook"
}
}
}
}
Then:
openclaw gateway restart
openclaw voicecall setup
i go with ngrok , give me full guide of setup
Yep — here’s the full ngrok path for OpenClaw voice-call/Twilio.
Important first: free ngrok URLs are not permanent. They change when the tunnel restarts. For a permanent URL, use an ngrok reserved/static domain. If you’re okay updating config each time, free is fine for testing.
1) Install ngrok in WSL
From WSL Ubuntu:
curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc \
| sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null
echo "deb https://ngrok-agent.s3.amazonaws.com bookworm main" \
| sudo tee /etc/apt/sources.list.d/ngrok.list
sudo apt update
sudo apt install ngrok
Check:
ngrok version
2) Add your ngrok auth token
Create/login at https://dashboard.ngrok.com, copy your auth token, then:
ngrok config add-authtoken YOUR_NGROK_TOKEN
3) Get a permanent ngrok domain
In ngrok dashboard:
Domains → New Domain / Reserved Domain
You’ll get something like:
your-name.ngrok.app
4) Configure OpenClaw voice-call
Edit:
nano ~/.openclaw/openclaw.json
Inside your voice-call plugin config, use this shape:
plugins: {
entries: {
"voice-call": {
enabled: true,
config: {
provider: "twilio",
fromNumber: "+YOUR_TWILIO_NUMBER",
serve: {
port: 3334,
path: "/voice/webhook"
},
tunnel: {
provider: "ngrok",
ngrokDomain: "your-name.ngrok.app"
},
twilio: {
accountSid: "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authToken: "your_twilio_auth_token"
}
}
}
}
}
If you already have twilio, fromNumber, etc configured, don’t duplicate them — just add/update the serve and tunnel parts.
5) Restart Gateway
openclaw gateway restart
If you’re using dev mode instead:
cd ~/openclaw-main
pnpm gateway:watch
6) Verify voice-call setup
openclaw voicecall setup
You want webhook exposure to be green and public URL to look like:
https://your-name.ngrok.app/voice/webhook
7) Dry-run, then real call
Dry run:
openclaw voicecall smoke --to "+RECIPIENT_NUMBER"
Real test call:
openclaw voicecall smoke --to "+RECIPIENT_NUMBER" --yes
Or your normal call:
openclaw voicecall call --to "+RECIPIENT_NUMBER" --message "Hello from OpenClaw" --mode notify
Docs: https://docs.openclaw.ai/plugins/voice-call
If setup still fails, paste the openclaw voicecall setup output — it usually says exactly which piece is missing.
i have one dev domain in dashboard -> domain .... is it permanent or what?
Yes — if it’s listed in ngrok Dashboard → Domains as your dev/static domain, it’s meant to be stable/permanent for your account, unlike random ngrok-free.app URLs created by ngrok http 3334.
Use the hostname exactly as shown, for example:
something.ngrok-free.app
In OpenClaw config:
tunnel: {
provider: "ngrok",
ngrokDomain: "something.ngrok-free.app"
}
Then OpenClaw should resolve the public webhook as:
https://something.ngrok-free.app/voice/webhook
Restart and check:
openclaw gateway restart
openclaw voicecall setup
Small caveat: it’s “permanent” as long as ngrok keeps that domain assigned to your account/plan. For normal dev/testing, that’s exactly what you want.
"voice-call": {
"enabled": true,
"config": {
"provider": "twilio",
"twilio": {
"accountSid": "something",
"authToken": "something"
},
"fromNumber": "no"
configure it ...
Yep — structure it like this. Don’t paste your real Twilio auth token in Discord; keep that private.
{
plugins: {
entries: {
"voice-call": {
enabled: true,
config: {
provider: "twilio",
twilio: {
accountSid: "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authToken: "your_twilio_auth_token"
},
// Must be your Twilio phone number, in E.164 format
fromNumber: "+1234567890",
serve: {
port: 3334,
path: "/voice/webhook"
},
tunnel: {
provider: "ngrok",
ngrokDomain: "your-dev-domain.ngrok-free.app"
},
outbound: {
defaultMode: "notify"
}
}
}
}
}
}
Replace:
ACxxxxxxxx... = Twilio Account SID
your_twilio_auth_token = Twilio Auth Token
+1234567890 = your Twilio number, not “no”
your-dev-domain.ngrok-free.app = your ngrok domain from dashboard
Then run:
openclaw gateway restart
openclaw voicecall setup
If setup is green, test dry-run first:
openclaw voicecall smoke --to "+RECIPIENT_NUMBER"
Then real call:
openclaw voicecall smoke --to "+RECIPIENT_NUMBER" --yes
ayu@MSI:~$ openclaw voicecall setup
🦞 OpenClaw 2026.5.8 (3f0c0ce) — Claws out, commit in—let's ship something mildly responsible.
Voice Call setup: OK
OK plugin-enabled: Voice Call plugin is enabled
OK provider: Provider configured: twilio
OK provider-config: Provider credentials/config look complete
OK webhook-exposure: Webhook exposure configured through tunnel
OK mode: Notify/conversation calls use normal TTS/STT flow
ayu@MSI:~$
Perfect — that means the voice-call plugin + Twilio creds + ngrok webhook are configured correctly ✅
Now do a dry run first:
openclaw voicecall smoke --to "+RECIPIENT_NUMBER"
Then place a real test call:
openclaw voicecall smoke --to "+RECIPIENT_NUMBER" --yes
Or directly:
openclaw voicecall call --to "+RECIPIENT_NUMBER" --message "Hello from OpenClaw" --mode notify
Use phone numbers in E.164 format, like +15551234567.
If the call starts but no audio/callback happens, check:
openclaw voicecall status
openclaw logs --follow
openclaw voicecall call --to "+RECIPIENT_NUMBER" --message "Hello from OpenClaw" --mode notify
did this and it popped a call-id there
Nice — getting a call-id means OpenClaw accepted the call request and created/tracked the call ✅
Now check what happened with it:
openclaw voicecall status --call-id "PASTE_CALL_ID_HERE"
Or list active/recent calls:
openclaw voicecall status
If you want logs while testing:
openclaw logs --follow
For --mode notify, it should place a call and speak the message. If the phone didn’t ring, the next things to check are:
tonumber is real E.164 format, e.g.+15551234567fromNumberis your actual Twilio number- Twilio trial accounts can only call verified numbers
- Twilio console call logs may show blocked/failed reasons
Paste the voicecall status --call-id ... output if it didn’t ring or didn’t speak.
ayu@MSI:~$ openclaw voicecall status --call-id "3a1ea9af-1448-4115-950b-1a1ce2f540b3"
🦞 OpenClaw 2026.5.8 (3f0c0ce) — Type the command with confidence—nature will provide the stack trace if needed.
{
"found": false
}
ayu@MSI:~$ openclaw voicecall status
🦞 OpenClaw 2026.5.8 (3f0c0ce) — Finally, a use for that always-on Mac Mini under your desk.
{
"found": true,
"calls": []
}
ayu@MSI:~$
That output isn’t automatically bad. voicecall status usually shows active/in-memory calls; a short --mode notify call can finish quickly and disappear from the active list.
Next check whether Twilio actually accepted/completed/failed it:
openclaw logs --follow
Then place another test call in a second terminal:
openclaw voicecall call --to "+RECIPIENT_NUMBER" --message "Hello from OpenClaw" --mode notify
Watch the logs for Twilio errors/status callbacks.
Also check Twilio Console → Monitor → Logs → Calls. If it failed, Twilio will usually say why. Common causes:
- trial Twilio account calling an unverified number
fromNumberis not your Twilio numbertonumber isn’t E.164 format like+15551234567- insufficient Twilio balance/permissions
- webhook/tunnel works in setup, but the ngrok process/Gateway stopped before callback
If you paste the exact output from the original voicecall call ... command plus the relevant log lines after it, I can tell which case it is.
okay so i get a call on my phone but it got disconnected when i picked up and only ring 4-5s
Good sign: Twilio can reach your phone now. The pickup → instant disconnect usually means Twilio accepted the call but then got no usable TwiML/audio flow, or the call ended quickly after notify mode.
Check the exact failure reason in Twilio first:
Twilio Console → Monitor → Logs → Calls → click that call
Look for Error Code, Call Status, and any webhook/TwiML errors.
Also run this while making another test call:
openclaw logs --follow
Then test with a longer notify message so it doesn’t immediately finish:
openclaw voicecall call --to "+YOUR_PHONE" --message "Hello from OpenClaw. This is a longer test message. If you can hear this, the Twilio voice call setup is working correctly." --mode notify
If it still disconnects instantly, paste:
openclaw logs --follow
relevant lines plus the Twilio call log error code. The most common causes are:
- Twilio trial account restrictions / unverified recipient number
- Twilio cannot fetch or validate webhook after pickup
- ngrok tunnel restarted and URL changed
fromNumberisn’t the Twilio number- TTS/STT config issue if using conversation mode
For notify mode, it should at least speak the message and then hang up.
18:39:53+00:00 info gateway/ws {"subsystem":"gateway/ws"} ⇄ res ✓ voicecall.initiate 879ms conn=58fb0dd2…2841 id=87922b1a…801d
when i pick the call it got disconnected
That log line only says OpenClaw successfully requested the call. It doesn’t show what happened after pickup.
Also: you used --mode notify. Notify mode is a one-way call: Twilio says the message, then the call ends. If the message is short, it can feel like “picked up → disconnected”.
Test with a longer message:
openclaw voicecall call --to "+YOUR_PHONE" --message "Hello from OpenClaw. This is a longer test message. If you can hear this sentence, the Twilio call setup is working correctly." --mode notify
If you want it to stay open as a conversation, try:
openclaw voicecall call --to "+YOUR_PHONE" --message "Hello, this is OpenClaw." --mode conversation
While testing, tail only voice-call/Twilio logs:
openclaw logs --follow | grep -Ei 'voice-call|twilio|call'
And check Twilio Console → Monitor → Logs → Calls → click the call. If Twilio has an error code there, paste it. That will tell us if it’s normal notify hangup vs webhook/TwiML/TTS failure.
ayu@MSI:~$ openclaw logs --follow | grep -Ei 'voice-call|twilio|call'
2026-05-09T18:33:47.183Z info gateway/ws {"subsystem":"gateway/ws"} ⇄ res ✓ voicecall.initiate 426ms conn=ccff899f…75e2 id=7cf772a5…a3f4
2026-05-09T18:34:46.683Z info gateway/ws {"subsystem":"gateway/ws"} ⇄ res ✓ voicecall.initiate 438ms conn=0cf34924…44d7 id=d5a90088…0ed3
2026-05-09T18:39:53.310Z info gateway/ws {"subsystem":"gateway/ws"} ⇄ res ✓ voicecall.initiate 879ms conn=58fb0dd2…2841 id=87922b1a…801d
Log tail truncated (increase --max-bytes).
2026-05-09T18:44:43.579Z info gateway/ws {"subsystem":"gateway/ws"} ⇄ res ✓ voicecall.initiate 487ms conn=7fcd0dc1…a5b6 id=dc2e6f0e…2c16
still hung up
That grep is only showing the UI/RPC request (voicecall.initiate), not the Twilio provider details. We need the real call failure reason.
Run these instead:
openclaw voicecall tail
And also:
openclaw logs --max-bytes 200000 | grep -Ei 'voice-call|twilio|Outbound call|TwiML|webhook|error|warn'
Then make one more test call with a long message:
openclaw voicecall call --to "+YOUR_PHONE" --message "Hello from OpenClaw. This is a longer test message. If you can hear this, the call setup is working correctly." --mode notify
If it still hangs up immediately, the fastest answer is in Twilio:
Twilio Console → Monitor → Logs → Calls → click latest call
Paste the Error Code / Warning / Request Inspector info. Twilio will usually say something like invalid TwiML, blocked trial call, webhook failure, etc.
For notify mode OpenClaw sends inline TwiML like “Say this message, then Hangup”. So if it rings but disconnects without speaking, it’s very likely Twilio rejected/failed the TwiML or ended the call before audio playback. The Twilio call log is the source of truth here.
{
"message": "Call is not in-progress. Cannot redirect. ",
"code": 21220,
"userError": true,
"params": {},
"httpStatusCode": -1
}
its showing 200 and 400
21220 means Twilio is saying: OpenClaw tried to redirect/update the call, but Twilio already considered the call no longer in progress.
So the webhook/tunnel part is working — otherwise you wouldn’t get the phone call. This is now a call-state / TwiML flow issue.
Two things to check:
-
In Twilio Console → latest Call → check Call duration and Request Inspector.
- If duration is
0sor1s, Twilio ended before OpenClaw could speak. - If you see a 400 with code
21220, that’s the failed redirect/update.
- If duration is
-
Run one test in pure notify mode with a long message:
openclaw voicecall call --to "+YOUR_PHONE" --message "Hello from OpenClaw. This is a long notify test. If you can hear this sentence, Twilio text to speech is working. The call will hang up after this message." --mode notify
For notify, it should speak then hang up. If it still hangs up silently, paste the Twilio Request Inspector entry for the 200 request — especially the TwiML/response body if shown.
Also grab full voice-call logs without only the gateway RPC line:
openclaw logs --max-bytes 300000 | grep -Ei 'voice-call|twilio|TwiML|21220|warn|error|answered|completed'
My read: 21220 is not the root “can’t call” problem anymore; it’s OpenClaw/Twilio trying to update a call after it already ended. Need the Twilio request inspector to see why it ends immediately.
If duration is 0s or 1s yes its true
Yep — 0s/1s means Twilio is ending the call immediately after answer. Since it rings, your Twilio credentials + ngrok reachability are mostly working. Now we need to isolate whether this is Twilio/account/number or OpenClaw call flow.
Do this quick isolation test directly against Twilio, not OpenClaw:
export TWILIO_ACCOUNT_SID="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export TWILIO_AUTH_TOKEN="your_auth_token"
export TWILIO_FROM="+YOUR_TWILIO_NUMBER"
export TEST_TO="+YOUR_PHONE"
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Calls.json" \
--data-urlencode "To=$TEST_TO" \
--data-urlencode "From=$TWILIO_FROM" \
--data-urlencode 'Twiml=<Response><Say>Hello. This is a direct Twilio test.</Say><Pause length="2"/></Response>' \
-u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN"
If this also rings then instantly disconnects, it’s a Twilio/account/number issue — common ones:
- trial account calling a non-verified number
fromNumbernot actually owned by your Twilio account- region/geo permissions blocked
- Twilio console has a specific error in the call log
If the direct Twilio test speaks correctly, then OpenClaw’s voice-call flow is hitting a race/redirect issue. In that case paste the Twilio Request Inspector for the OpenClaw call, especially the 200 request/response body and the 400 with code 21220.
Also check Twilio Console → Monitor → Logs → Calls → latest call → Warnings / Errors / Request Inspector. That page will usually name the exact cause.
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Calls.json"
--data-urlencode "To=$TEST_TO"
--data-urlencode "From=$TWILIO_FROM"
--data-urlencode 'Twiml=<Response><Say>Hello. This is a direct Twilio test.</Say><Pause length="2"/></Response>'
-u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN"
this thing worked successfully
Great — that proves Twilio itself is fine ✅
So the problem is specifically OpenClaw’s voice-call flow, not your Twilio number/phone/account.
Most likely OpenClaw is not sending the call as simple inline notify TwiML, or it’s trying to redirect/update the call after Twilio already ended it — that matches the 21220 error.
Try this exact OpenClaw CLI call, not through the agent/UI:
openclaw voicecall call --to "+YOUR_PHONE" --message "Hello. This is an OpenClaw notify test using Twilio." --mode notify
Then immediately run:
openclaw logs --max-bytes 300000 | grep -Ei 'voice-call|twilio|inline TwiML|notify|21220|error|warn'
You should see lines like:
[voice-call] Using inline TwiML for notify mode
[voice-call] Sending direct Twilio initial TwiML ... kind=notify
If you don’t see those, OpenClaw isn’t actually using notify inline TwiML.
Also make sure config has this:
outbound: {
defaultMode: "notify",
notifyHangupDelaySec: 3
}
If the exact CLI command still rings then drops silently while direct Twilio works, that’s likely an OpenClaw voice-call bug/regression. Grab those log lines plus the Twilio Request Inspector and file it here: https://github.com/openclaw/openclaw/issues
still disconnecting on pickup and gateway logs only have
18:45:55+00:00 info gateway/ws {"subsystem":"gateway/ws"} ⇄ res ✓ voicecall.initiate 424ms conn=cca975c9…0988 id=c6ef5571…cc1b
Since direct Twilio curl works, this is now definitely inside the OpenClaw voice-call path.
The missing bit is: your log only shows the UI/CLI RPC request succeeded. It does not show whether the voice-call plugin generated inline notify TwiML.
Please run these and paste output:
openclaw voicecall tail
Then:
journalctl --user -u openclaw-gateway.service -n 300 --no-pager | grep -Ei 'voice-call|twilio|TwiML|notify|21220|error|warn'
If you’re running pnpm gateway:watch instead of the systemd service, use:
tmux capture-pane -pt openclaw-gateway-watch-main -S -300 | grep -Ei 'voice-call|twilio|TwiML|notify|21220|error|warn'
For a healthy notify call, we expect to see something like:
[voice-call] Using inline TwiML for notify mode
[voice-call] Sending direct Twilio initial TwiML ... kind=notify
If those lines are missing, OpenClaw isn’t actually sending the simple notify TwiML, even though the RPC says voicecall.initiate succeeded.
Also paste the exact output from this command:
openclaw voicecall call --to "+YOUR_PHONE" --message "Hello from OpenClaw notify test." --mode notify
And if possible, Twilio Console → latest call → Request Inspector → copy the 200 response body / TwiML. That will tell us whether Twilio received <Say>... or an empty/redirect response.
{"callId":"814d0c4f-00c8-42f8-b72f-70e419685f9d","provider":"twilio","direction":"outbound","state":"ringing","from":"+646756576","to":"+67676858","sessionKey":"voice:67476476646","startedAt":1778353487165,"transcript":[],"processedEventIds":["twilio:req:0957b9aaef082763971760aca11c97be04b85170289886ea2b9be7ad455e20aa","twilio:req:241f2a271608eb697ccb3ddafc659a3d63758ae6c91730661b607326394500de"],"metadata":{"initialMessage":"Hello. This is an OpenClaw notify test using Twilio.meow meow meow","mode":"notify"},"providerCallId":"CA6b8dc6c013e97235d215613b722eca69"}
{"callId":"814d0c4f-00c8-42f8-b72f-70e419685f9d","provider":"twilio","direction":"outbound","state":"speaking","from":"+19785431134","to":"+7587587","sessionKey":"voice:78579678","startedAt":1778353487165,"transcript":[],"processedEventIds":["twilio:req:0957b9aaef082763971760aca11c97be04b85170289886ea2b9be7ad455e20aa","twilio:req:241f2a271608eb697ccb3ddafc659a3d63758ae6c91730661b607326394500de","twilio:req:891816925e8a5b38c7c1b347eac8f4ea22f3a560b6b8cb43282ec660ea22dfb7"],"metadata":{"initialMessage":"Hello. This is an OpenClaw notify test using Twilio.meow meow meow","mode":"notify"},"providerCallId":"CA6b8dc6c013e97235d215613b722eca69","answeredAt":1778353498397}
{"callId":"814d0c4f-00c8-42f8-b72f-70e419685f9d","provider":"twilio","direction":"outbound","state":"completed","from":"+759675785","to":"+785785876","sessionKey":"voice:7675476475","startedAt":1778353487165,"transcript":[{"timestamp":1778353498787,"speaker":"bot","text":"Hello. This is an OpenClaw notify test using Twilio.meow meow meow","isFinal":true}],"processedEventIds":["twilio:req:0957b9aaef082763971760aca11c97be04b85170289886ea2b9be7ad455e20aa","twilio:req:241f2a271608eb697ccb3ddafc659a3d63758ae6c91730661b607326394500de","twilio:req:891816925e8a5b38c7c1b347eac8f4ea22f3a560b6b8cb43282ec660ea22dfb7","twilio:req:06b2010a47a857dd046d405b4b8e8f486fe3a88ce27f8e0c2489ad6927751473"],"metadata":```
This tail is actually useful: OpenClaw is receiving Twilio callbacks and sees the call go:
ringing → speaking → completed
So ngrok/webhook is working. The suspicious part is timing: it marks the bot message as spoken almost immediately after answeredAt. That means notify mode is probably ending too fast / racing the Twilio playback.
Try increasing the notify hangup delay:
plugins: {
entries: {
"voice-call": {
enabled: true,
config: {
outbound: {
defaultMode: "notify",
notifyHangupDelaySec: 15
}
}
}
}
}
Then restart:
openclaw gateway restart
Test again:
openclaw voicecall call --to "+YOUR_PHONE" --message "Hello from OpenClaw. This is a longer test message with a longer hangup delay." --mode notify
If it still silently disconnects, try conversation mode:
openclaw voicecall call --to "+YOUR_PHONE" --message "Hello from OpenClaw." --mode conversation
My read: Twilio is fine, webhook is fine, but OpenClaw notify mode is likely hanging up before Twilio audio is actually heard. If notifyHangupDelaySec: 15 doesn’t fix it, this smells like a voice-call plugin bug/race worth filing: https://github.com/openclaw/openclaw/issues
Also: if any of those phone numbers were real, rotate/redact in future logs before posting publicly.
"voice-call": {
"enabled": true,
"config": {
"provider": "twilio",
"twilio": {
"accountSid": "sid",
"authToken": "token"
},
"fromNumber": "+no",
"serve": {
"port": 3334,
"path": "/voice/webhook"
},
"tunnel": {
"provider": "ngrok",
"ngrokDomain": "domain"
},
"outbound": {
"defaultMode": "notify"
}
}
},
Yep — add notifyHangupDelaySec inside outbound:
"voice-call": {
"enabled": true,
"config": {
"provider": "twilio",
"twilio": {
"accountSid": "sid",
"authToken": "token"
},
"fromNumber": "+YOUR_TWILIO_NUMBER",
"serve": {
"port": 3334,
"path": "/voice/webhook"
},
"tunnel": {
"provider": "ngrok",
"ngrokDomain": "your-domain.ngrok-free.app"
},
"outbound": {
"defaultMode": "notify",
"notifyHangupDelaySec": 15
}
}
}
Then restart:
openclaw gateway restart
Test again with a longer message:
openclaw voicecall call --to "+YOUR_PHONE" --message "Hello from OpenClaw. This is a longer notify test with a longer hangup delay." --mode notify
Also make sure fromNumber is your real Twilio number in E.164 format, like +15551234567 — not +no.
still problem , can i switch twilio to any open source provider??? like prison cell?
Not currently, no — the voice-call plugin supports:
twilio | telnyx | plivo | mock
mock is only local/dev testing; it won’t call a real phone number. Telnyx/Plivo are alternatives to Twilio, but they’re still hosted telecom providers, not open-source/self-hosted.
If by “prison cell” you mean something like FreeSWITCH / Asterisk / FreePBX / SIP, OpenClaw doesn’t have a built-in provider for that today. That would need a new voice-call provider/plugin implementation.
Given your direct Twilio curl works but OpenClaw notify calls disconnect, I’d treat this as a likely OpenClaw voice-call bug/race and file it with:
- OpenClaw version:
2026.5.8 (3f0c0ce) - provider: Twilio
- direct Twilio inline TwiML works
- OpenClaw call reaches
ringing → speaking → completedbut phone hears nothing / disconnects - Twilio error
21220 Call is not in-progress. Cannot redirect