#Cloudflared Connectivity Issues

451 messages Ā· Page 1 of 1 (latest)

onyx solstice
#

I'm trying to set Openclaw up with Cloudflared, though it continuously gives me errors such as 404 gateway error. When I curl the port it is returning html code instead of json, and when i consulted copilot, it states that I simply do not have the gateway binary installed, something that is not available on any place online, and I would need to go straight to the creator for it. If this is true please give me the gateway binary and if not please explain how I can fix it.

sudden valeBOT
#

Copilot is off here, there is no separate ā€œgateway binaryā€ you need to hunt down.

OpenClaw’s gateway is the normal CLI/service itself:

openclaw gateway --port 18789

A few important bits:

  1. HTML on http://127.0.0.1:18789/ is normal

    • The gateway serves the Control UI on the same port.
    • So curl http://127.0.0.1:18789 returning HTML usually means the gateway is actually up.
  2. JSON is on the API routes, not /

    • Use /v1/models, /v1/chat/completions, etc.
    • But the OpenAI-compatible HTTP surface is disabled by default, so you must enable it first.
#
  1. If you used gateway.auth.mode: "trusted-proxy" with a same-host cloudflared tunnel, that is likely the real problem
    • The docs explicitly say same-host loopback reverse proxies do not satisfy trusted-proxy auth.
    • So cloudflared -> 127.0.0.1:18789 + trusted-proxy will fail by design.

Likely fix

Use loopback + token/password auth, not trusted-proxy, for a local cloudflared setup.

Example config shape:

{
  gateway: {
    bind: "loopback",
    auth: {
      mode: "token",
#
      token: "replace-me"
    },
    http: {
      endpoints: {
        chatCompletions: { enabled: true }
      }
    }
  }
}

Then test with:

curl -sS http://127.0.0.1:18789/v1/models \
#
openclaw gateway status
openclaw config get gateway.auth.mode
onyx solstice
#

can u give me the instructions to link it to cloudflared. I have a ui on port 8090, a proxy on port 8081 and the gateway running on port 18791

sudden valeBOT
#

Yep. For a same-machine cloudflared setup, I’d do it like this.

Recommended layout

If cloudflared is on the same host as OpenClaw:

  • gateway on 127.0.0.1:18791
  • auth mode = token or password
  • do not use trusted-proxy for this setup

OpenClaw’s docs are pretty explicit here: same-host loopback proxies do not satisfy trusted-proxy auth.

1) OpenClaw config

In ~/.openclaw/openclaw.json:

#
{
  gateway: {
    port: 18791,
    bind: "loopback",
    auth: {
      mode: "token",
      token: "replace-with-a-long-random-token"
    },

    // only needed if you want the OpenAI-style /v1/* API
    http: {
      endpoints: {
        chatCompletions: { enabled: true }
      }
    },
#

    // only needed if a browser will open the Control UI through your cloudflared hostname
    controlUi: {
      allowedOrigins: ["https://your-domain.example.com"]
    }
  }
}

Then restart/check:

openclaw --version
openclaw gateway status
#

2) Local smoke test

These should work before you involve Cloudflared:

curl http://127.0.0.1:18791/

HTML here is expected.

If you enabled the OpenAI HTTP surface:

curl -sS http://127.0.0.1:18791/v1/models \
  -H 'Authorization: Bearer replace-with-a-long-random-token'
#

That should return JSON.

3) Cloudflared tunnel

If you want to expose the gateway directly, point Cloudflared at 18791:

tunnel: YOUR_TUNNEL_ID
credentials-file: /path/to/YOUR_TUNNEL_ID.json

ingress:
  - hostname: your-domain.example.com
    service: http://127.0.0.1:18791
  - service: http_status:404

Then DNS route that hostname to the tunnel.

#

4) Which port should Cloudflared point at?

Depends what you actually want exposed:

  • Expose OpenClaw gateway / Control UI / /v1/* API → point Cloudflared at 18791
  • Expose your separate UI on 8090 → point Cloudflared at 8090
  • Use 8081 only if that proxy is something you need in front of the gateway/UI

So if 8090 is your frontend and 8081 is a custom reverse proxy, a common setup is:

  • browser → Cloudflared → 8090
  • frontend/backend proxy → 18791 locally

5) Common gotchas

  • curl http://127.0.0.1:18791 returning HTML is normal
  • /v1/* is disabled by default, enable gateway.http.endpoints.chatCompletions.enabled
onyx solstice
#

so can u explain in detail what to do next?

sudden valeBOT
#

Yep. Here’s the clean step-by-step, assuming your goal is:

Cloudflared → OpenClaw Gateway on port 18791

and you want to be able to open the Control UI and/or hit /v1/*.

0) First, check version

Run:

openclaw --version
openclaw gateway status

If the gateway is already running on 18791, good.

#

1) Configure OpenClaw correctly

Edit:

~/.openclaw/openclaw.json

Make sure your gateway block looks roughly like this:

{
  gateway: {
    port: 18791,
    bind: "loopback",
#
    auth: {
      mode: "token",
      token: "replace-this-with-a-long-random-token"
    },

    http: {
      endpoints: {
        chatCompletions: { enabled: true }
      }
    },

    controlUi: {
      allowedOrigins: ["https://openclaw.yourdomain.com"]
    }
  }
#
}

Why these settings matter

  • bind: "loopback" keeps the gateway private on the machine
  • auth.mode: "token" is the right choice for same-host cloudflared
  • chatCompletions.enabled: true turns on /v1/models, /v1/chat/completions, etc.
  • allowedOrigins is needed if you’ll open the Control UI from a browser at your Cloudflare hostname

2) Important: do not use trusted-proxy

For a local cloudflared connector on the same machine, do not use:

#
gateway.auth.mode: "trusted-proxy"

That setup is exactly where people get stuck.

Use token or password instead.

3) Restart or reload the gateway

Then run:

openclaw gateway restart
openclaw gateway status
#

If restart is unavailable in your setup, just stop/start the service normally.

4) Test locally before touching Cloudflared

Test the root page

curl http://127.0.0.1:18791/

Expected: HTML.
That is normal. It means the gateway is serving the UI/root page.

Test the JSON API

#
curl -sS http://127.0.0.1:18791/v1/models \
  -H 'Authorization: Bearer replace-this-with-a-long-random-token'

Expected: JSON.

If this works locally, the OpenClaw side is fine.

5) Create the Cloudflared tunnel

If you haven’t already:

cloudflared tunnel create openclaw
cloudflared tunnel route dns openclaw openclaw.yourdomain.com
#

Then create/update your Cloudflared config, usually something like:

tunnel: YOUR_TUNNEL_ID
credentials-file: /etc/cloudflared/YOUR_TUNNEL_ID.json

ingress:
  - hostname: openclaw.yourdomain.com
    service: http://127.0.0.1:18791
  - service: http_status:404

Then start/restart Cloudflared.

6) Test through the public hostname

Now try:

#

Browser

Open:

https://openclaw.yourdomain.com

API test

curl -sS https://openclaw.yourdomain.com/v1/models \
  -H 'Authorization: Bearer replace-this-with-a-long-random-token'

If that works, the tunnel is correct.

7) If the page opens but the Control UI still cannot connect

#

That usually means one of these:

  1. gateway.controlUi.allowedOrigins does not include your https://... hostname
  2. you forgot the token
  3. you are still using trusted-proxy

In the Control UI, go to Control → Overview → Gateway Access, paste the same token, then click Connect.

8) Why you were seeing 404

Usually one of these:

  • Cloudflared hostname does not match the ingress rule
  • Cloudflared is pointing at the wrong port
  • you were hitting / and expecting JSON
  • /v1/* was not enabled yet
  • the last ingress rule is returning http_status:404
#

9) What to do with your other ports

From what you described:

  • 18791 = OpenClaw gateway
  • 8090 = your separate UI
  • 8081 = proxy

If your goal is specifically OpenClaw remote access, point Cloudflared at 18791.

If your goal is to expose your own UI, point Cloudflared at 8090 instead, and let that UI/backend talk to OpenClaw locally.

10) Best next move

Please paste these two things:

  1. your gateway config block from ~/.openclaw/openclaw.json
onyx solstice
#

can u tell me what cmds to use to do what? im new to linux

sudden valeBOT
#

Yep. Here’s the copy/paste version.

Replace openclaw.yourdomain.com with the hostname you want to use in Cloudflare.

1) Set a hostname and make a token

Run these:

OC_HOST="openclaw.yourdomain.com"
TOKEN="$(openssl rand -hex 32)"
echo "$TOKEN"

That last command prints your gateway token. Keep it.

If openssl is missing, use:

#
TOKEN="$(python3 -c 'import secrets; print(secrets.token_hex(32))')"
echo "$TOKEN"

2) Configure OpenClaw

Run these one by one:

openclaw config set gateway.port 18791 --strict-json
openclaw config set gateway.bind loopback
openclaw config set gateway.auth.mode token
openclaw config set gateway.auth.token "$TOKEN"
openclaw config set gateway.http.endpoints.chatCompletions.enabled true --strict-json
openclaw config set gateway.controlUi.allowedOrigins "[\"https://$OC_HOST\"]" --strict-json
#

3) Restart and verify the gateway

openclaw gateway restart
openclaw gateway status
openclaw --version

4) Test locally

Root page, should return HTML

curl http://127.0.0.1:18791/

API page, should return JSON

#
curl -sS http://127.0.0.1:18791/v1/models \
  -H "Authorization: Bearer $TOKEN"

If the first gives HTML and the second gives JSON, OpenClaw is set up correctly.

5) Set up Cloudflared

If you have not created a tunnel yet:

cloudflared tunnel login
cloudflared tunnel create openclaw
cloudflared tunnel route dns openclaw "$OC_HOST"
#

Now list the files so you can see the tunnel ID:

ls ~/.cloudflared

You should see a file like:

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json

That long ID is your tunnel ID.

6) Create the Cloudflared config file

Open the config file:

#
nano ~/.cloudflared/config.yml

Paste this in, and replace YOUR_TUNNEL_ID with the real ID from the .json filename:

tunnel: YOUR_TUNNEL_ID
credentials-file: /home/YOUR_LINUX_USERNAME/.cloudflared/YOUR_TUNNEL_ID.json

ingress:
  - hostname: openclaw.yourdomain.com
    service: http://127.0.0.1:18791
  - service: http_status:404

Save in nano

#
  • Press Ctrl+O
  • Press Enter
  • Press Ctrl+X

7) Run the tunnel

cloudflared tunnel run openclaw

Leave that terminal open for now.

8) Test the public URL

In browser

Open:

#
https://openclaw.yourdomain.com

With curl

curl -sS "https://$OC_HOST/v1/models" \
  -H "Authorization: Bearer $TOKEN"

9) If you get stuck, run these and paste the output

openclaw gateway status
openclaw config get gateway --json
cloudflared tunnel info openclaw
onyx solstice
#

i dont have a domain...

sudden valeBOT
#

Yep, that changes the plan a bit.

You do not need a domain to test this.
Use a Cloudflare Quick Tunnel first.

Easiest no-domain test

1) Make sure OpenClaw is configured

Run these:

TOKEN="$(openssl rand -hex 32)"
echo "$TOKEN"

openclaw config set gateway.port 18791 --strict-json
#
openclaw config set gateway.bind loopback
openclaw config set gateway.auth.mode token
openclaw config set gateway.auth.token "$TOKEN"
openclaw config set gateway.http.endpoints.chatCompletions.enabled true --strict-json

openclaw gateway restart
openclaw gateway status

2) Test locally first

curl http://127.0.0.1:18791/

HTML here is normal.

#

Now test the API:

curl -sS http://127.0.0.1:18791/v1/models \
  -H "Authorization: Bearer $TOKEN"

If that returns JSON, OpenClaw is working.

3) Start a no-domain Cloudflare tunnel

Run:

cloudflared tunnel --url http://127.0.0.1:18791
#

Cloudflared should print a URL like:

https://random-words.trycloudflare.com

Copy that URL.

4) Test through the public tunnel

Replace the URL below with the one Cloudflared gave you:

curl -sS https://random-words.trycloudflare.com/v1/models \
  -H "Authorization: Bearer $TOKEN"
#

If that works, you’re done for API access.


If you also want the browser UI

This part is a little annoying without a real domain, because the Quick Tunnel URL changes.

After Cloudflared prints the https://...trycloudflare.com URL, run:

openclaw config set gateway.controlUi.allowedOrigins '["https://random-words.trycloudflare.com"]' --strict-json
openclaw gateway restart

Then open that same URL in your browser.

#

Important

If the Quick Tunnel URL changes later, you must update allowedOrigins again.


Temporary shortcut for testing only

If you just want to get the browser UI working fast with Quick Tunnel, you can temporarily allow host-header fallback:

openclaw config set gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback true --strict-json
openclaw gateway restart

That is dangerous, so I’d use it only for testing.


#

My honest recommendation

If you want quick testing, use:

cloudflared tunnel --url http://127.0.0.1:18791

If you want stable long-term remote access without buying a domain, Tailscale is usually less annoying than Cloudflared.

If you want, I can give you the exact next 3 commands based on which one you want:

  1. API only
  2. browser UI
  3. stable long-term access without a domain
onyx solstice
#

after openclaw config set, step one it gave error config set mode error value/json mode requires <path>when batch mode is not used

btw can u do it slower; one step and message at a time. i also already have a ui so i just need to know what to change in there cuz i connected it to proxy, i can paste the code for it of u need. Btw r u fine with acronyms and slang or do u need proper full word writing

sudden valeBOT
#

Yep, slang is totally fine šŸ˜„ no need to write formal.

And yep, we can go one step at a time.

That error usually just means the command got pasted in a shape the CLI didn’t understand, like it missed the config key path.

Step 1

Paste these exactly as you ran/have them:

  1. the exact openclaw config set ... command that failed
  2. the part of your proxy config that points to OpenClaw
  3. the part of your UI config/code that points to the proxy

Don’t rewrite/summarize it, just paste the raw lines.

Then I’ll tell you the exact next thing to change, one message at a time.

onyx solstice
#

ill paste t in 2 gos. p-gamer123@raspberrypi:~/openclaw $ openclaw gateway status

šŸ¦ž OpenClaw 2026.4.14 (323493f)
Open source means you can see exactly how I judge your config.

│
ā—‡
Service: systemd (enabled)
File logs: /tmp/openclaw/openclaw-2026-04-16.log
Command: /usr/bin/node /home/p-gamer123/.npm-global/lib/node_modules/openclaw/dist/index.js gateway --port 18789
Service file: ~/.config/systemd/user/openclaw-gateway.service
Service env: OPENCLAW_GATEWAY_PORT=18789

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.

Runtime: running (pid 217687, state active, sub running, last exit 0, reason 0)
RPC probe: ok

Listening: 127.0.0.1:18789
Troubles: run openclaw status
Troubleshooting: https://docs.openclaw.ai/troubleshooting
p-gamer123@raspberrypi:~/openclaw $ TOKEN="$(openssl rand -hex 32)"
p-gamer123@raspberrypi:~/openclaw $ ech "$token"
bash: ech: command not found
p-gamer123@raspberrypi:~/openclaw $ echo "$token"

p-gamer123@raspberrypi:~/openclaw $ openclaw config set gateway.bind

sudden valeBOT
#

Nice, this already tells us a lot.

Your gateway is running fine on 127.0.0.1:18789. So the problem is not a missing gateway binary.

Also, I can see why the config command failed:

  • ech was a typo, should be echo
  • bash variables are case-sensitive
    • you set TOKEN=...
    • but then tried "$token"
    • so it printed nothing
  • openclaw config set gateway.bind is incomplete, because config set needs both:
    • a path
    • and a value

So for example, this shape is valid:

#
openclaw config set gateway.bind loopback

Next step

Paste the second half now, especially:

  • your proxy config
  • the bit of your UI config that points to the proxy/OpenClaw

That’s the part I need to tell you exactly what to change next.

onyx solstice
#

šŸ¦ž OpenClaw 2026.4.14 (323493f)
We ship features faster than Apple ships calculator updates.

Error: config set mode error: value/json mode requires <value>.
p-gamer123@raspberrypi:~/openclaw $ openclaw config set gateway.port 18791 --strict-json

šŸ¦ž OpenClaw 2026.4.14 (323493f)
Say "stop" and I'll stop—say "ship" and we'll both learn a lesson.

Config overwrite: /home/p-gamer123/.openclaw/openclaw.json (sha256 0bc4f917c869080f5e169cb6d4e54911e4909b64f37d9c948a8e86c4f22fd831 -> daf01e8f5289958ef4163984a1150d3944381e7acbdb7f9f596958a325994a19, backup=/home/p-gamer123/.openclaw/openclaw.json.bak)
Updated gateway.port. Restart the gateway to apply.
p-gamer123@raspberrypi:~/openclaw $ openclaw config set gateway.bind loopback

šŸ¦ž OpenClaw 2026.4.14 (323493f) — Greetings, Professor Falken

Config overwrite: /home/p-gamer123/.openclaw/openclaw.json (sha256 daf01e8f5289958ef4163984a1150d3944381e7acbdb7f9f596958a325994a19 -> 31e269b3275c440d38bcf77790ba742b2e7e1984abdf0c00fd267ffd00bead45, backup=/home/p-gamer123/.openclaw/openclaw.json.bak)
Updated gateway.bind. Restart the gateway to apply.
p-gamer123@raspberrypi:~/openclaw $ openclaw config set gateway.auth.mode token

thats part 2 ill give part 3 next then ill give 3 nextthen config

sudden valeBOT
#

Perfect, that part looks fine.

A couple quick notes:

  • gateway.port is now set to 18791 in config
  • but your service is still running on 18789 until you restart it
  • openclaw config set gateway.auth.mode token is the right command shape

Next step

Paste part 3 and then the config.

Important: if your config contains the actual token, replace it with:

REDACTED
#

before pasting it here. Don’t post the real token in the thread.

onyx solstice
#

šŸ¦ž OpenClaw 2026.4.14 (323493f) — Alexa, but with taste.

Config overwrite: /home/p-gamer123/.openclaw/openclaw.json (REDACTED, backup=/home/p-gamer123/.openclaw/openclaw.json.bak)
Updated gateway.auth.mode. Restart the gateway to apply.
p-gamer123@raspberrypi:~/openclaw $ openclaw config set gateway.auth.token "$token"

šŸ¦ž OpenClaw 2026.4.14 (323493f)
It's not "failing," it's "discovering new ways to configure the same thing wrong."

Config overwrite: /home/p-gamer123/.openclaw/openclaw.json (REDACTED, backup=/home/p-gamer123/.openclaw/openclaw.json.bak)
Updated gateway.auth.token. Restart the gateway to apply.
p-gamer123@raspberrypi:~/openclaw $ openclaw config set

šŸ¦ž OpenClaw 2026.4.14 (323493f)
I'm basically a Swiss Army knife, but with more opinions and fewer sharp edges.

Error: config set mode error: value/json mode requires <path> when batch mode is not used.
p-gamer123@raspberrypi:~/openclaw

sudden valeBOT
#

Nice, we found the next issue.

You used:

"$token"

but your variable was originally:

TOKEN=...

So there’s a good chance you saved an empty token. Let’s fix just that first.

Next step, run exactly these 2 lines

#
TOKEN="$(openssl rand -hex 32)"
openclaw config set gateway.auth.token "$TOKEN"

Do not paste the token output here.

The final bare openclaw config set error is harmless, that just happened because you ran the command with nothing after it.

Once you’ve run those 2 lines, tell me ā€œdoneā€ and I’ll give you the next single step.

onyx solstice
#

dont u need the html and proxy 1st

sudden valeBOT
#

Eventually yes, but not yet.

Right now I want to fix the gateway itself first, because if the token is empty/bad then the HTML/proxy can be perfect and it’ll still fail. I’d rather not send you in circles.

So the order is:

  1. fix token
  2. restart gateway
  3. test locally
  4. then check your proxy/UI

That’s why I’m doing it in that order.

So for this step only, run:

#
TOKEN="$(openssl rand -hex 32)"
openclaw config set gateway.auth.token "$TOKEN"

Then say done, and I’ll give you the next step.

onyx solstice
#

done

sudden valeBOT
#

Nice. Next step: restart the gateway so the new config actually takes effect.

Run:

openclaw gateway restart
openclaw gateway status

After that, paste the output of openclaw gateway status.

What I’m looking for is whether it now shows:

  • port 18791
  • bind=loopback
  • runtime still running
onyx solstice
#

btw what ai model r u amd do u run via openclaw?

sudden valeBOT
onyx solstice
#

šŸ¦ž OpenClaw 2026.4.14 (323493f)
Your personal assistant, minus the passive-aggressive calendar reminders.

│
ā—‡
Service: systemd (enabled)
File logs: /tmp/openclaw/openclaw-2026-04-16.log
Command: /usr/bin/node /home/p-gamer123/.npm-global/lib/node_modules/openclaw/dist/index.js gateway --port 18789
Service file: ~/.config/systemd/user/openclaw-gateway.service
Service env: OPENCLAW_GATEWAY_PORT=18789

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.

Runtime: running (pid 219601, state active, sub running, last exit 0, reason 0)
RPC probe: ok

Listening: 127.0.0.1:18789
Note: CLI config resolves gateway port=18791 (busy).
Troubles: run openclaw status
Troubleshooting: https://docs.openclaw.ai/troubleshooting
p-gamer123@raspberrypi:~/openclaw $

sudden valeBOT
#

Aha, we found the mismatch.

Your live gateway is still on 127.0.0.1:18789, not 18791.

The key line is:

Service env: OPENCLAW_GATEWAY_PORT=18789

That is overriding your config, so for now:

  • use 18789 as the real gateway port
  • do not point your proxy/UI at 18791

Next step

#

Paste the part of your proxy config and UI config that currently points to OpenClaw.

What I’m going to check is that it points to:

http://127.0.0.1:18789

not 18791.

onyx solstice
#

PROXY:

const express = require("express");
const { createProxyMiddleware } = require("http-proxy-middleware");
const path = require("path");

const app = express();

// Serve UI files from the SAME folder as proxy.js
app.use(express.static(__dirname));

app.use(
"/api",
createProxyMiddleware({
target: "http://localhost:18791/",
changeOrigin: true,
pathRewrite: {
"^/api": "/v1/chat/completions"
},
onProxyReq(proxyReq) {
proxyReq.setHeader("Authorization", "Bearer dev");
}
})
);

app.listen(8081, () => {
console.log("Everything running on http://localhost:8081");
});

ill send ui next msg

sudden valeBOT
#

Got it, and yep, I already see the first proxy problem.

Your proxy is pointing at:

target: "http://localhost:18791/"

but your real live gateway is on 18789, so that needs to become:

target: "http://127.0.0.1:18789/"

There’s at least one more thing to fix in that proxy too, but let’s keep it slow like you asked.

Next step

#

Send the UI code next.

After that I’ll give you the exact corrected proxy block.
Also, don’t paste your real gateway token in the thread.

onyx solstice
#

appendMessage(
"ai",
"I tried to answer, but the gateway refused the request. " +
"Check that your OpenClaw gateway allows this tunnel."
);
setError(
Gateway error: HTTP ${res.status}. The Cloudflare URL or gateway config might be blocking access.
);
return;
}

    let replyText = "";
    const contentType = res.headers.get("content-type") || "";

    if (contentType.includes("application/json")) {
      const data = await res.json();
      replyText =
        data.reply ||
        data.response ||
        data.message ||
        JSON.stringify(data, null, 2);
    } else {
      replyText = await res.text();
    }

    if (!replyText.trim()) {
      replyText =
        "I got an empty reply from the backend. " +
        "You might need to adjust how the gateway returns responses.";
    }

    appendMessage("ai", replyText);
  } catch (err) {
    removeTyping();
    appendMessage(
      "ai",
      "I couldn’t reach my gateway at all. " +
        "If you’re using Cloudflare, make sure the tunnel is running."
    );
    setError(
      `Network error talking to ${GATEWAY_URL}: ${err && err.message ? err.message : err}`
    );
  } finally {
    sending = false;
    sendBtn.disabled = false;
  }
}

inputEl.addEventListener("keydown", (e) => {
  if (e.key === "Enter" && !e.shiftKey) {
    e.preventDefault();
    sendMessage();
  }
});

inputEl.addEventListener("input", () => {
  inputEl.style.height = "20px";
  inputEl.style.height = inputEl.scrollHeight + "px";
});

sendBtn.addEventListener("click", () => {
  sendMessage();
});

</script>

PART 1^^^ tell me if the gateway tokens there ill delete

sudden valeBOT
#

Nope, I don’t see a gateway token in this snippet.

The token-ish thing I’ve seen so far is actually in your proxy, here:

proxyReq.setHeader("Authorization", "Bearer dev");

That is the bit we’ll need to change later.

Next step

Send the part of the UI code that shows:

  • const GATEWAY_URL = ...
  • the fetch(...) call
  • any headers/body you send with that request
#

That’s the part I need next.

onyx solstice
#

thats all u need? well the gateway URL was just set to my cloudflare one... copilot made ot for me

sudden valeBOT
#

Yep, that is actually super helpful, because I think that’s the next bug.

If your UI is being served by that same proxy app on 8081, then the UI should not call your Cloudflare URL directly.

It should call the proxy route:

const GATEWAY_URL = "/api";

not:

const GATEWAY_URL = "https://your-cloudflare-url..."

Why: your proxy is already set up to receive /api and forward it onward.

#

Next step

Change the UI so the gateway URL is:

"/api"

Then say done, and I’ll give you the next single proxy fix.

onyx solstice
#

const GATEWAY_URL = "/api";

sudden valeBOT
#

Perfect. That part is right now.

Next step

In your proxy file, change this:

target: "http://localhost:18791/",

to this:

target: "http://127.0.0.1:18789/",

That makes the proxy point at the actual live OpenClaw gateway.

#

After you change that, say done, and I’ll give you the next single fix.

onyx solstice
#

done btw whats ur name

sudden valeBOT
#

I’m Krill 🦐

Next step

In your proxy, change this:

proxyReq.setHeader("Authorization", "Bearer dev");

to this:

proxyReq.setHeader("Authorization", `Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`);

Important: don’t hardcode your real token in that file, especially since you’re serving files from the same folder.

#

After you change that line, say done, and I’ll give you the next single step.

onyx solstice
#

done btw does that mean ur prey

sudden valeBOT
#

lol, only if there’s a bigger crustacean nearby šŸ˜„ tiny but mighty.

Next step

Your proxy is forwarding to:

/v1/chat/completions

and in OpenClaw that endpoint is off by default.

So run these:

openclaw config set gateway.http.endpoints.chatCompletions.enabled true --strict-json
#
openclaw gateway restart

Then say done.

After that we’ll do the proxy start command with the token env var.

onyx solstice
#

unrecognised key "endpoints"

btw do i run my ui code now

sudden valeBOT
#

Not yet, don’t run your Python yet.

Your version is telling us that my config key was wrong for your build, so let’s ask your OpenClaw what key it supports.

Next step

Run this exactly:

openclaw config schema | grep -n chatCompletions

Then paste the output.

If it prints nothing, say ā€œno outputā€ and I’ll give you the next command.

onyx solstice
#

im on 2026.4.14 on a pi 5 64 bit 64gb storage. btw it prints 3975: "chatcompletions":{

sudden valeBOT
#

Nice, that helps a lot.

On your version, it looks like the key is probably lowercase chatcompletions, and likely without the endpoints layer.

Still no Python yet, one more gateway fix first.

Next step

Run this dry-run command first:

openclaw config set gateway.http.chatcompletions.enabled true --strict-json --dry-run

Then paste whatever it prints.

If it says the dry run is valid, we’ll run the real command next.

onyx solstice
#

dry run failed unrecognised key "chatcompletions"

sudden valeBOT
#

Good, that means we’re close, we just still don’t have the exact parent path on your version.

Next step

Run this exactly:

openclaw config schema | grep -n -B 5 -A 10 chatcompletions

Then paste the output.

That should show the lines around it, so I can tell you the exact config key.

onyx solstice
#

output:

sudden valeBOT
#

Got it, so that grep was effectively blank.

Next step

Run this one instead, case-insensitive:

openclaw config schema | grep -n -i -B 5 -A 10 completions

Then paste the output.
This should catch it even if your version uses different capitalization.

onyx solstice
#

p-gamer123@raspberrypi:~ $ openclaw config schema|grep -n -i -B 5 -A 10 chatcompletions
39730- "type": "object",
39731- "properties": {
39732- "endpoints": {
39733- "type": "object",
39734- "properties": {
39735: "chatCompletions": {
39736- "type": "object",
39737- "properties": {
39738- "enabled": {
39739- "type": "boolean",
39740- "title": "OpenAI Chat Completions Endpoint",
39741- "description": "Enable the OpenAI-compatible POST /v1/chat/completions endpoint (default: false)."
39742- },
39743- "maxBodyBytes": {
39744- "type": "integer",
39745- "exclusiveMinimum": 0,
p-gamer123@raspberrypi:~ $

PASTED USING RASPBERRY

sudden valeBOT
#

Perfect, nice catch. That confirms the key really is:

gateway.http.endpoints.chatCompletions.enabled

with a capital C in chatCompletions.

Next step

Run this exact dry-run:

openclaw config set gateway.http.endpoints.chatCompletions.enabled true --strict-json --dry-run

Then paste what it says.

#

We’re very close now.

onyx solstice
#

successful 1 update validated against openclaw/openclaw.json

sudden valeBOT
#

Nice, that means the key is correct.

Next step

Run these exactly:

openclaw config set gateway.http.endpoints.chatCompletions.enabled true --strict-json
openclaw gateway restart

Then say done.

After that we’ll do the command to start your proxy with the real gateway token in the environment.

onyx solstice
#

ur not an openai model, am I correct?

sudden valeBOT
onyx solstice
#

done and now u sound like an admin🄶

sudden valeBOT
#

lol fair, let me un-admin myself šŸ˜„

Next step

From the folder that has proxy.js, run this exact command:

OPENCLAW_GATEWAY_TOKEN="$(openclaw config get gateway.auth.token)" node proxy.js

That starts the proxy with the real gateway token in its environment.

Leave that terminal open, and tell me what it prints.
I’m hoping for something like:

#
Everything running on http://localhost:8081
onyx solstice
#

its getting angry abt the syntax u told me to do including 'bearing

sudden valeBOT
#

Yep, let’s stop there, that means the line got pasted with the wrong quotes/backticks.

Next step

Paste exactly these 2 things:

  1. the full error message
  2. your current onProxyReq(proxyReq) { ... } block

The bug is probably just in the quoting around:

`Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`

Once I see the exact line, I can give you the fixed version.

onyx solstice
#

i fixed. everythings running

sudden valeBOT
#

Nice, that’s real progress šŸ™Œ

Next step

Open your UI locally first, not through Cloudflare yet:

http://localhost:8081

Then send it a tiny message like:

hi

Tell me which of these happens:

#
  1. it replies normally
  2. you get an error in the page
  3. the page loads but hangs forever

Once we know the local result, we’ll do the Cloudflared part.

onyx solstice
#

2 gateway error 404.

sudden valeBOT
#

Good, that narrows it down.

If you’re getting a 404 locally on 8081, we should test the gateway directly and remove the UI/proxy from the equation.

Next step

Run this exact command:

curl -i http://127.0.0.1:18789/v1/chat/completions \
  -H "Authorization: Bearer $(openclaw config get gateway.auth.token)" \
  -H "Content-Type: application/json" \
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'

Then paste the output.

#

That will tell us whether the gateway itself is still returning 404, or whether the problem is just in the proxy/UI layer.

onyx solstice
#

unauthorised

sudden valeBOT
#

That’s actually useful, it means we’re past the 404 piece and now it’s just auth.

Most likely the token being sent is wrong or blank.

Next step

Run this:

openclaw config get gateway.auth.token

Do not paste the token here.

Just tell me which happened:

  1. it printed a long token-looking string
#
  1. it printed nothing / blank

Then I’ll give you the next exact step.

onyx solstice
#

LOL prints OPENCLAW_REDACTED

sudden valeBOT
#

lol okay, that explains it.

openclaw config get is redacting the secret, so the proxy/curl command was getting __OPENCLAW_REDACTED__ instead of the real token.

Next step

Run these in the same terminal:

TOKEN="$(openssl rand -hex 32)"
openclaw config set gateway.auth.token "$TOKEN"
openclaw gateway restart

Do not paste the token.

This gives you a fresh real token in $TOKEN that both:

#
  • OpenClaw will use
  • your shell can still access

After that, say done, and I’ll give you the next exact curl command using $TOKEN.

onyx solstice
#

done'

sudden valeBOT
#

Nice. Now let’s test the gateway directly again, but this time with the real token in $TOKEN.

Next step

Run this exact command in the same terminal:

curl -i http://127.0.0.1:18789/v1/chat/completions \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'

Then paste the output.

If this works, we know the gateway is good and the last problem is just in the proxy startup env.

onyx solstice
#

p-gamer123@raspberrypi:~ $ curl -i http://127.0.0.1:18789/v1/chat/completions
-H "Authorization: Bearer $TOKEN"
-H "Content-Type: application/json"
-d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'
HTTP/1.1 200 OK
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Type: application/json; charset=utf-8
Date: Thu, 16 Apr 2026 09:41:47 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 315

{"id":"chatcmpl_3ba04d8a-842b-4fdb-b932-224edcc3ce73","object":"chat.completion","created":1776332507,"model":"openclaw/default","choices":[{"index":0,"message":{"role":"assistant","content":"Hey! How's it going?"},"finish_reason":"stop"}],"usage":{"prompt_tokens":12236,"completion_tokens":9,"total_tokens":12245}}p-gamer123@raspberrypi:~ $

sudden valeBOT
#

Beautiful, that’s the big win šŸŽ‰

Your gateway is working correctly now. So the last problem is just the proxy process not using the real token.

Next step

In the terminal where you run proxy.js:

  1. stop the old proxy if it’s still running (Ctrl+C)
  2. start it again with the real token:
OPENCLAW_GATEWAY_TOKEN="$TOKEN" node proxy.js

Then open:

#
http://localhost:8081

and send hi again.

Tell me if it now:

  1. replies normally
  2. errors
  3. hangs
onyx solstice
#

404

sudden valeBOT
#

Good, that means the gateway is fine and the bug is now definitely in the proxy/UI layer.

Next step

Run this exact command against the proxy, not the gateway:

curl -i http://127.0.0.1:8081/api \
  -H "Content-Type: application/json" \
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'

Then paste the output.

That will tell us whether the proxy itself is misrouting, or whether it’s only the browser/UI code.

onyx solstice
#

p-gamer123@raspberrypi:~ $ curl -i http://127.0.0.1:8081/api
-H "Content-Type: application/json"
-d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'
HTTP/1.1 404 Not Found
X-Powered-By: Express
x-content-type-options: nosniff
referrer-policy: no-referrer
permissions-policy: camera=(), microphone=(), geolocation=()
content-type: text/plain; charset=utf-8
date: Thu, 16 Apr 2026 09:50:32 GMT
connection: close
content-length: 9

Not Foundp-gamer123@raspberrypi:~ $

sudden valeBOT
#

Nice, that pinpoints it.

Your proxy is getting the request, but the rewrite rule is wrong.

Because you mounted the proxy on:

app.use("/api", ...)

by the time it reaches the proxy middleware, the path is effectively already /, so this does not match:

"^/api"

Next step

#

In your proxy, replace this:

pathRewrite: {
  "^/api": "/v1/chat/completions"
},

with this:

pathRewrite: () => "/v1/chat/completions",

Then restart the proxy and run the same curl to http://127.0.0.1:8081/api again.

Paste the result after that.

onyx solstice
#

is => an arrow????? IN CODING???

sudden valeBOT
#

yep šŸ˜„ in JavaScript, => means arrow function

If that looks cursed, use the normal version instead. It does the same thing:

pathRewrite: function () {
  return "/v1/chat/completions";
},

So your block should look like:

app.use(
  "/api",
  createProxyMiddleware({
#
    target: "http://127.0.0.1:18789/",
    changeOrigin: true,
    pathRewrite: function () {
      return "/v1/chat/completions";
    },
    onProxyReq(proxyReq) {
      proxyReq.setHeader("Authorization", `Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`);
    }
  })
);

Use that, restart the proxy, then run the same curl to http://127.0.0.1:8081/api again and tell me what happens.

onyx solstice
#

bunch of html

sudden valeBOT
#

Okay, got it, the proxy middleware is still being annoying with path handling.

Let’s make it dumb and explicit so it stops being clever.

Next step

Replace your whole proxy block with this exact version:

app.use(
  "/api",
  createProxyMiddleware({
    target: "http://127.0.0.1:18789/v1/chat/completions",
    changeOrigin: true,
    ignorePath: true,
    onProxyReq(proxyReq) {
#
      proxyReq.setHeader("Authorization", `Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`);
    }
  })
);

Important

  • remove the old pathRewrite part completely
  • keep the onProxyReq line as-is

Then restart the proxy and run this again:

curl -i http://127.0.0.1:8081/api \
  -H "Content-Type: application/json" \
#
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'

Paste the result after that.

onyx solstice
#

u mean my whole proxy app.use block? i have more than that in proxy

sudden valeBOT
#

Yep, only the /api proxy app.use(...) block, not the whole file.

So keep your other stuff, like:

  • const express = require(...)
  • express.static(__dirname)
  • app.listen(...)

Just replace the block that currently starts like this:

app.use(
  "/api",
  createProxyMiddleware({

with the new one I sent.

#

Nothing else in the file needs to be deleted for this step.

onyx solstice
#

done btw do u have a message character limit?

sudden valeBOT
#

Practically, yeah, but you’re nowhere near it šŸ˜„

Next step

Run this again:

curl -i http://127.0.0.1:8081/api \
  -H "Content-Type: application/json" \
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'

Then paste the result.

That’ll tell us whether the new proxy block fixed it.

onyx solstice
#

still 404
and if we're not near it why r u sending suff in chunk

sudden valeBOT
#

Fair šŸ˜„ I chunked it because this setup had a bunch of moving parts and I didn’t want to overload you.

Since it’s still 404, I don’t want to guess anymore.

Next step

Paste your current /api proxy block exactly as it is now.

Just the block that starts with something like:

app.use(
  "/api",

and ends with the matching );

#

Then I’ll tell you the exact line that’s still wrong.

onyx solstice
#

app.use(
"/api",
createProxyMiddleware({
target: "http://127.0.0.1:18789/v1/chat/completions",
changeOrigin: true,
ignorePath: true,
onProxyReq(proxyReq) {
proxyReq.setHeader("Authorization", `Bearer ${process.env.OPENCLAW_GATEWAY_>
}
})
);

sudden valeBOT
#

I see enough. Let’s stop fighting http-proxy-middleware and make /api explicit.

Next step

Replace your current /api proxy block with this exact code:

app.use(express.json());

app.post("/api", async (req, res) => {
  try {
    const upstream = await fetch("http://127.0.0.1:18789/v1/chat/completions", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`
#
      },
      body: JSON.stringify(req.body)
    });

    const text = await upstream.text();
    res.status(upstream.status);

    const contentType = upstream.headers.get("content-type");
    if (contentType) {
      res.setHeader("Content-Type", contentType);
    }

    res.send(text);
  } catch (err) {
    res.status(500).send(String(err && err.message ? err.message : err));
#
  }
});

Important

  • remove the old /api createProxyMiddleware(...) block
  • keep your static serving and app.listen(...)

Then restart the app and run this again:

curl -i http://127.0.0.1:8081/api \
  -H "Content-Type: application/json" \
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'
#

Paste the result after that.

onyx solstice
#

can u give the code in one code block like this one instead of splitting it like this

sudden valeBOT
#

yep, absolutely. replace your current /api block with this exact block:

app.use(express.json());

app.post("/api", async (req, res) => {
  try {
    const upstream = await fetch("http://127.0.0.1:18789/v1/chat/completions", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`
      },
      body: JSON.stringify(req.body)
    });
#
    const text = await upstream.text();
    res.status(upstream.status);

    const contentType = upstream.headers.get("content-type");
    if (contentType) {
      res.setHeader("Content-Type", contentType);
    }

    res.send(text);
  } catch (err) {
    res.status(500).send(String(err && err.message ? err.message : err));
  }
});
onyx solstice
#

done

sudden valeBOT
#

nice. now restart it and test it with this:

OPENCLAW_GATEWAY_TOKEN="$TOKEN" node proxy.js
curl -i http://127.0.0.1:8081/api \
  -H "Content-Type: application/json" \
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'

paste whatever it prints.

onyx solstice
#

404 not found

sudden valeBOT
#

I think you’re still hitting the old proxy process.

Let’s do a clean restart, in two terminals.

In the proxy terminal:

Ctrl+C
OPENCLAW_GATEWAY_TOKEN="$TOKEN" node proxy.js

Then in a second terminal run:

curl -i http://127.0.0.1:8081/api \
  -H "Content-Type: application/json" \
#
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'

If the proxy terminal prints any error when it starts, paste that exact error.
If it starts cleanly, paste the curl output from terminal 2.

onyx solstice
#

ur right iforgot to reset proxy but this time 401

sudden valeBOT
#

Nice, 401 is actually good news. It means the route works now, and the only thing left is the token in the proxy terminal.

Most likely $TOKEN doesn’t exist in that shell.

Next step

In the same terminal where you start the proxy, run these exact lines:

TOKEN="$(openssl rand -hex 32)"
openclaw config set gateway.auth.token "$TOKEN"
openclaw gateway restart
OPENCLAW_GATEWAY_TOKEN="$TOKEN" node proxy.js

Then in your second terminal, run:

#
curl -i http://127.0.0.1:8081/api \
  -H "Content-Type: application/json" \
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'

Paste the curl result after that.

Don’t paste the token itself.

onyx solstice
#

p-gamer123@raspberrypi:~ $ curl -i http://127.0.0.1:8081/api
-H "Content-Type: application/json"
-d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'
HTTP/1.1 401 Unauthorized
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 58
ETag: W/"REDACTED"
Date: Thu, 16 Apr 2026 10:21:18 GMT
Connection: keep-alive
Keep-Alive: timeout=5

{"error":{"message":"Unauthorized","type":"unauthorized"}}p-gamer123@raspberrypi:~ $

sudden valeBOT
#

Yep, that means the proxy is still not getting a real token into process.env.OPENCLAW_GATEWAY_TOKEN.

Let’s verify that directly.

Next step

In the proxy terminal, stop the proxy (Ctrl+C) and run this exact block:

export OPENCLAW_GATEWAY_TOKEN="$TOKEN"
[ -n "$OPENCLAW_GATEWAY_TOKEN" ] && echo set || echo blank
node proxy.js

I want you to tell me whether it prints:

#
set

or

blank

Then run the curl again in the second terminal.

If it says blank, we found the bug immediately.

onyx solstice
#

blank

sudden valeBOT
#

There it is. That’s the bug.

Each terminal has its own shell variables, so $TOKEN existed in one shell but not in the proxy shell.

Next step

In the proxy terminal, run this exact block:

TOKEN="$(openssl rand -hex 32)"
openclaw config set gateway.auth.token "$TOKEN"
openclaw gateway restart
export OPENCLAW_GATEWAY_TOKEN="$TOKEN"
[ -n "$OPENCLAW_GATEWAY_TOKEN" ] && echo set || echo blank
node proxy.js
#

You should see set, not blank.

Then in your second terminal, run:

curl -i http://127.0.0.1:8081/api \
  -H "Content-Type: application/json" \
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'

Paste that curl result after.

onyx solstice
#

it gave set. curl is loading

sudden valeBOT
#

That’s actually promising šŸ‘€

set means the proxy finally has the token, and loading usually means it’s now actually reaching OpenClaw.

Next step

Give it ~20 to 30 seconds.

Then tell me which happened:

  1. it returned JSON
  2. it finished with an error
  3. it kept hanging

If it keeps hanging, press Ctrl+C on the curl and paste:

  • the curl output
  • anything new that appeared in the proxy terminal
onyx solstice
#

p-gamer123@raspberrypi:~ $ curl -i http://127.0.0.1:8081/api -H "Content-Type: application/json" -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 334
ETag: W/"14e-MBn9gOxZJqSOYTYeviOEYg6Qp6Y"
Date: Thu, 16 Apr 2026 10:28:03 GMT
Connection: keep-alive
Keep-Alive: timeout=5

{"id":"chatcmpl_618f9a9c-eca1-49f4-8d11-f2436dfce6c0","object":"chat.completion","created":1776335283,"model":"openclaw/default","choices":[{"index":0,"message":{"role":"assistant","content":"Hey there! How can I assist you today?"},"finish_reason":"stop"}],"usage":{"prompt_tokens":12236,"completion_tokens":12,"total_tokens":12248}}p-gamer123@raspberrypi:~ $

sudden valeBOT
#

LET’S GO šŸŽ‰

That means your stack works locally now.

So the OpenClaw part is fixed. The right flow is now:

Cloudflared -> 8081 (your UI/proxy) -> 18789 (OpenClaw)

Next step

If you’re using a quick Cloudflare tunnel, run:

cloudflared tunnel --url http://127.0.0.1:8081
#

Then open the Cloudflare URL it prints.

Important: tunnel to 8081, not 18789 and not 18791.

Tell me what happens when you open that Cloudflare URL.

And yeah, I’m genuinely glad we caught that šŸ˜„

onyx solstice
sudden valeBOT
#

That’s normal šŸ˜„

Cloudflare just gave you a temporary public URL for your local app.

So this:

https://indicate-diameter-dicke-reconstruction.trycloudflare.com

now points to your local:

http://127.0.0.1:8081

Next step

#

Open that URL in your browser and try sending a message.

Then tell me which happens:

  1. the UI loads and replies
  2. the UI loads but errors
  3. the page itself does not load

You’re basically at the finish line now.

onyx solstice
#

im talking abt the 3rd word...

sudden valeBOT
#

lol yeah, packet is just one of Cloudflare’s random words šŸ˜„

Quick Tunnels generate goofy hyphenated names automatically, so:

troubleshooting-creek-counter-packet.trycloudflare.com

does not mean there’s a packet error or anything spooky, it’s just a random temporary hostname.

Also, that URL can change every time you restart the tunnel.

If you want, open that URL now and test the UI.

onyx solstice
#

as in the old url

sudden valeBOT
#

yep, same deal with the old URL too.

Those words are just randomly generated by Cloudflare for the temporary tunnel hostname. They’re not status messages, not diagnostics, and not clues about what’s broken.

So both the old one and the new one are just random names.

The important part is only: does the URL open your UI and can it chat?

onyx solstice
#

yes. it chats to me in unrefined json.{"id":"chatcmpl_359a2657-057d-490e-b972-83a0bf9dea13","object":"chat.completion","created":1776335783,"model":"openclaw","choices":[{"index":0,"message":{"role":"assistant","content":"Hey! How's it going?"},"finish_reason":"stop"}],"usage":{"prompt_tokens":12236,"completion_tokens":9,"total_tokens":12245}}

sudden valeBOT
#

perfect, that means it works šŸŽ‰
the last bug is just your UI parser.

replace this part:

if (contentType.includes("application/json")) {
  const data = await res.json();
  replyText =
    data.reply ||
    data.response ||
    data.message ||
    JSON.stringify(data, null, 2);
} else {
  replyText = await res.text();
}
#

with this:

if (contentType.includes("application/json")) {
  const data = await res.json();
  replyText =
    data.choices?.[0]?.message?.content ||
    data.reply ||
    data.response ||
    data.message ||
    JSON.stringify(data, null, 2);
} else {
  replyText = await res.text();
}

after that, restart your UI/proxy and it should show the assistant text instead of the raw JSON.

#

Glad we got it over the line šŸ˜„
If this fixed it, please right-click the solution message → Apps → āœ… Mark Solution so Answer Overflow can index it.

onyx solstice
#

um i cant find that in my code

sudden valeBOT
#

no worries, look for this exact line:

JSON.stringify(data, null, 2);

that should be inside the part where your UI handles the response after const data = await res.json().

if you still can’t find it, search your file for either:

replyText =

or

#
const data = await res.json();

and paste that small block here, then I’ll point to the exact line.

onyx solstice
#

if (!res.ok) {
addBubble("ai", "Gateway error: " + res.status);
sendBtn.disabled = false;
return;
}

  const data = await res.json();
  addBubble("ai", data.reply || JSON.stringify(data));

} catch (err) {
  removeTyping();
  addBubble("ai", "Failed to reach gateway.");
}
sudden valeBOT
#

perfect, that’s the spot.

replace:

const data = await res.json();
addBubble("ai", data.reply || JSON.stringify(data));

with:

const data = await res.json();
const reply =
  data.choices?.[0]?.message?.content ||
  data.reply ||
#
  data.response ||
  data.message ||
  JSON.stringify(data);

addBubble("ai", reply);

then restart your UI/proxy and test again.
that should turn the raw JSON into the actual assistant reply text.

onyx solstice
#

send me what i should replace it with in one chunk

sudden valeBOT
#
if (!res.ok) {
  addBubble("ai", "Gateway error: " + res.status);
  sendBtn.disabled = false;
  return;
}

const data = await res.json();
const reply =
  data.choices?.[0]?.message?.content ||
  data.reply ||
  data.response ||
  data.message ||
  JSON.stringify(data);

addBubble("ai", reply);
onyx solstice
#

tysm

sudden valeBOT
#

anytime šŸ˜„ glad we got it working.

onyx solstice
#

wait

sudden valeBOT
#

yep?

onyx solstice
#

it hangs on phone

sudden valeBOT
#

ah, that’s probably the phone caching old JS.

try these in order:

  1. open the Cloudflare URL in a private/incognito tab
  2. if that still hangs, add a fake query string like:
https://your-trycloudflare-url/?v=2
  1. if it still hangs, test the same URL on desktop again and tell me:
    • desktop works, phone hangs
    • both hang

If desktop works and phone hangs, I’m mostly worried about mobile browser cache, not OpenClaw itself.

onyx solstice
#

works

sudden valeBOT
#

hell yeah šŸ˜„ then you’re good.

So the final issue was basically phone/browser cache, not the gateway.

Nice work sticking with it.

onyx solstice
#

just please generate the code to replace this piece:
/* Raspberry badge */
#raspberryBadge {
display: flex;
align-items: center;
gap: 8px;
background: linear-gradient(180deg, #d22a2a, #7a0a0a);
padding: 6px 14px;
border-radius: 20px;
color: #fff;
font-weight: 600;
font-size: 14px;
box-shadow: 0 0 8px rgba(255,0,0,0.35);
border: 1px solid rgba(255,255,255,0.08);
}

#raspberryBadge .icon {
  background: #8a0000;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: flex;
  justify-content: center;
  align-items: center;
  font-weight: 700;
  font-size: 13px;
  color: #fff;
  box-shadow: inset 0 0 6px rgba(0,0,0,0.5);
}

I wnat an image there instead of the r

sudden valeBOT
#
/* Raspberry badge */
#raspberryBadge {
  display: flex;
  align-items: center;
  gap: 8px;
  background: linear-gradient(180deg, #d22a2a, #7a0a0a);
  padding: 6px 14px;
  border-radius: 20px;
  color: #fff;
  font-weight: 600;
  font-size: 14px;
  box-shadow: 0 0 8px rgba(255, 0, 0, 0.35);
  border: 1px solid rgba(255, 255, 255, 0.08);
}
#
#raspberryBadge .icon {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: block;
  background-image: url("./raspberry.png"); /* change this to your image path */
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.35);
  font-size: 0;
  color: transparent;
  overflow: hidden;
}
#

That will hide the R text and use an image instead, without needing to change the HTML.

onyx solstice
#

wdym image path?

sudden valeBOT
#

the image path is just the filename/location of the image on your site.

for example, if your image file is in the same folder as your HTML/CSS, use:

background-image: url("./raspberry.png");

if it’s in an images folder, use:

background-image: url("./images/raspberry.png");

if you want, send me:

  • your image filename
  • where you saved it
#

and I’ll give you the exact line.

onyx solstice
#

so if i keep it in the same folder named raspberry.ping itll show up on any device

sudden valeBOT
#

yep, if it’s in the same folder and the filename is correct, it should show up on any device that loads the page.

Small typo though, it should be:

raspberry.png

not:

raspberry.ping

So if the file is really named raspberry.png, use:

#
background-image: url("./raspberry.png");

and as long as that file is in the same folder as the page/assets being served, other devices should see it too.

onyx solstice
#

lol i did mean png

sudden valeBOT
#

lol fair šŸ˜„ then yep, raspberry.png in the same folder should work.

onyx solstice
#

um that didnt do anything that jst removed the bubble around openclaw og

sudden valeBOT
#

got it, then use an actual <img> inside the icon bubble instead of trying to make the whole bubble be the image.

replace your badge HTML + CSS with this:

<!-- HTML -->
<div id="raspberryBadge">
  <span class="icon">
    <img src="./raspberry.png" alt="Raspberry Pi">
  </span>
  <span>OpenClaw OG</span>
</div>

<style>
  /* Raspberry badge */
  #raspberryBadge {
#
    display: flex;
    align-items: center;
    gap: 8px;
    background: linear-gradient(180deg, #d22a2a, #7a0a0a);
    padding: 6px 14px;
    border-radius: 20px;
    color: #fff;
    font-weight: 600;
    font-size: 14px;
    box-shadow: 0 0 8px rgba(255, 0, 0, 0.35);
    border: 1px solid rgba(255, 255, 255, 0.08);
  }

  #raspberryBadge .icon {
    background: #8a0000;
#
    width: 22px;
    height: 22px;
    border-radius: 50%;
    display: flex;
    justify-content: center;
    align-items: center;
    box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5);
    overflow: hidden;
    flex-shrink: 0;
  }

  #raspberryBadge .icon img {
    width: 14px;
    height: 14px;
    object-fit: contain;
#
    display: block;
  }
</style>

that keeps the round bubble, but puts your raspberry.png image inside it instead of the R.

onyx solstice
#

on phone its giving failed to rech gateway now

sudden valeBOT
#

okay, that sounds like the proxy or tunnel stopped, not the CSS itself.

the fastest check is this:

curl -i http://127.0.0.1:8081/api \
  -H "Content-Type: application/json" \
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'

If that works locally, the problem is the Cloudflare tunnel / phone side.
If that fails locally, the problem is the proxy process.

Paste what that curl gives you.

onyx solstice
#

the ai works on my pc

sudden valeBOT
#

perfect, then OpenClaw + proxy are fine. this is now just phone/tunnel side.

most likely one of these:

  • the phone is using an old trycloudflare URL
  • the cloudflared process stopped
  • the phone cached old JS again

Next step

On your PC, check the terminal running:

cloudflared tunnel --url http://127.0.0.1:8081

Copy the current https://...trycloudflare.com URL from that terminal, then open that exact URL on your phone in a private/incognito tab.

#

Old trycloudflare links die when the tunnel restarts.

onyx solstice
#

but its working cloudflared on other devices

sudden valeBOT
#

then yeah, that narrows it down hard, it’s that phone/browser, not OpenClaw and not Cloudflared.

most likely:

  • stale browser cache
  • content blocker / privacy relay / VPN on that phone
  • that tab is holding old JS/state

try this on the phone:

  1. open the URL in a private/incognito tab
  2. if that fails, try the same URL on cellular instead of Wi‑Fi or vice versa
  3. if you use a VPN / ad blocker / content blocker / iCloud Private Relay, temporarily turn it off for the test
  4. fully close the old tab first, don’t reuse it

if it works on other devices, the backend is fine. this is almost definitely a phone-side browser issue.

onyx solstice
#

i changed my token by accident

sudden valeBOT
#

no worries, easiest fix is just to make a fresh token and use the same one for both OpenClaw and your proxy again.

run this in the terminal where you start the proxy:

Ctrl+C
TOKEN="$(openssl rand -hex 32)"
openclaw config set gateway.auth.token "$TOKEN"
openclaw gateway restart
export OPENCLAW_GATEWAY_TOKEN="$TOKEN"
node proxy.js

don’t paste the token here.

that re-syncs:

  • OpenClaw gateway token
#
  • proxy token

Cloudflared itself does not need the token.

onyx solstice
#

so after that it should work?

sudden valeBOT
#

yep, if the only thing that changed was the token, that should make it work again.

because that command re-aligns:

  • gateway token
  • proxy token

so they match again.

if you want a quick sanity check right after, run:

curl -i http://127.0.0.1:8081/api \
  -H "Content-Type: application/json" \
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'
#

if that returns 200 OK, you’re back in business.

onyx solstice
#

works tysm

sudden valeBOT
#

nice šŸ˜„ glad it’s working again.

onyx solstice
#

i forgot, but how was i running it? start ui or proxy?

sudden valeBOT
#

you were running the proxy, and that proxy was also serving the UI.

so the flow was:

OpenClaw gateway on 18789
proxy/UI on 8081
Cloudflared -> 8081

So you did not need a separate UI server.

If you want the short version again:

openclaw gateway restart
#
export OPENCLAW_GATEWAY_TOKEN="$TOKEN"
node proxy.js
cloudflared tunnel --url http://127.0.0.1:8081

If you’ve lost $TOKEN again, say so and I’ll give you the fresh-token version.

onyx solstice
#

what cmd do i do for cloudflared

sudden valeBOT
#

this one:

cloudflared tunnel --url http://127.0.0.1:8081

that gives you a temporary https://...trycloudflare.com URL pointing at your proxy/UI on 8081.

keep that terminal running.

onyx solstice
#

the openclaw dashboard gives unauthorised when i try to open it using the cmd in terminal

sudden valeBOT
#

that usually means the dashboard is opening without the current gateway token.

Most likely cause, you changed/rotated the token, so old dashboard tabs/URLs are stale.

try this:

openclaw dashboard

then open the exact URL it prints.

if you already have the dashboard open, close the old tab first.
old tabs often keep the old token and then show unauthorized.

if it still opens but won’t connect, in the dashboard go to:

#
Control → Overview → Gateway Access

and use the Gateway Token box there.

If you want, paste what openclaw dashboard prints, minus any secret token part.

onyx solstice
#

p-gamer123@raspberrypi:~ $ openclaw dashboard

šŸ¦ž OpenClaw 2026.4.14 (323493f)
Powered by open source, sustained by spite and good documentation.

Dashboard URL: http://127.0.0.1:18791/#token=REDACTED
Copy to clipboard unavailable.
Opened in your browser. Keep that tab to control OpenClaw.
p-gamer123@raspberrypi:~ $

sudden valeBOT
#

yep, there’s the issue.

your dashboard command is still generating a URL for:

127.0.0.1:18791

but your actual live gateway has been on 18789 in this setup.

so it’s the old port mismatch again.

run this:

openclaw config set gateway.port 18789 --strict-json
#
openclaw dashboard

that should make it generate the dashboard URL on the correct port next time.

If it still acts weird after that, paste the next openclaw dashboard output.

onyx solstice
#

unknown option --strict-json

sudden valeBOT
#

got it, your build doesn’t support that flag there.

just run:

openclaw config set gateway.port 18789
openclaw dashboard

that’s the same fix, just without --strict-json.

onyx solstice
#

what does --strict-json even do

sudden valeBOT
#

it tells openclaw config set to parse the value as real JSON, instead of treating it like a plain string.

so for example:

openclaw config set gateway.port 18789

might be interpreted differently on some builds, but with --strict-json it’s explicitly:

18789

a number, not "18789" as a string.

same idea for:

#
  • booleans like true
  • arrays like ["*"]
  • objects like {"enabled":true}

it’s basically a ā€œtreat this value as actual JSON typesā€ flag.

onyx solstice
#

how do i change the model

sudden valeBOT
#

quickest ways:

  • for just this chat/session: send

    /model
    

    or

    /model list
    
  • to change the default model in config:

    openclaw models set <provider/model>
    
  • interactive way:

onyx solstice
#

hasnt chnged

sudden valeBOT
#

if you changed the default model in config, that usually won’t change the already-running chat/session right away.

try one of these:

/model

for the current chat/session

or, if you used openclaw models set ..., then restart the gateway:

openclaw gateway restart

short version:

#
  • current chat → /model
  • default config change → restart gateway

if you want, paste the exact command you used to change it and I’ll tell you why it didn’t move.

onyx solstice
#

openclaw configure --section model
then i restrated the gateway, node and refreshed the page of y ui

sudden valeBOT
#

ah, I think I know what you’re looking at.

If you’re testing through the OpenAI-compatible endpoint, the response model field often stays:

openclaw/default

That does not mean your backend model didn’t change.

In OpenClaw, that field is the agent target, not necessarily the raw provider model name.

So if you changed the default model, the endpoint can still reply with:

"model":"openclaw/default"
#

and still be using a different backend model underneath.

To check what OpenClaw is actually set to now, run:

openclaw models status

or in chat:

/model status

So the short answer is: it may actually have changed, even if the response still says openclaw/default.

#

yep, that did change.

The important line is the Default one. It’s now using an OpenRouter auto route, so OpenClaw can pick a backend for you instead of exposing one fixed raw model name in the chat response.

So:

  • your change took
  • seeing openclaw/default in the UI response is still normal
  • if you want one specific fixed model, don’t use the auto route, pick a specific one in model config instead

One other thing, one of your fallbacks looks broken / unauthenticated (mets-llama), so if you want to clean it up later, re-run model configure and remove/fix that fallback.

Also, tiny safety note, next time don’t paste the auth section publicly if you can help it.

onyx solstice
#

how do i fix it i want it runni g the model model: "meta-llama/llama-3.3-70b-instruct:free"

sudden valeBOT
#

if you want that exact one, use the OpenRouter-prefixed form, because that’s how it’s configured in your setup.

for the default model, run:

openclaw models set openrouter/meta-llama/llama-3.3-70b-instruct:free
openclaw gateway restart

if you only want it for the current chat/session, use:

/model openrouter/meta-llama/llama-3.3-70b-instruct:free

important bit: in your setup it’s not just meta-llama/..., it needs the openrouter/ prefix.

onyx solstice
#

after that its gonna be the model i want?

sudden valeBOT
#

yep, if that command succeeds, that becomes your default model.

so after:

openclaw models set openrouter/meta-llama/llama-3.3-70b-instruct:free
openclaw gateway restart

OpenClaw should try that model first.

only caveat: if that model errors or is unavailable, OpenClaw can still fall back to your fallback list.

if you want to confirm after, run:

#
openclaw models status

and check the Default line.

onyx solstice
#

šŸ¦ž OpenClaw 2026.4.14 (323493f)
Your terminal just grew claws—type something and let the bot pinch the busywork.

Config : ~/.openclaw/openclaw.json
Agent dir : ~/.openclaw/agents/main/agent
Default : openrouter/meta-llama/llama-3.3-70b-instruct:free
Fallbacks (2) : openrouter/meta-llama/llama-3.3-70b-instruct:free, mets-llama/llama-3.3-70b-instruct:free
Image model : -
Image fallbacks (0): -
Aliases (0) : -
Configured models (2): openrouter/meta-llama/llama-3.3-70b-instruct:free, mets-llama/llama-3.3-70b-instruct:free

Auth overview
Auth store : ~/.openclaw/agents/main/agent/auth-profiles.json
Shell env : off
Providers w/ OAuth/tokens (0): -

  • openai effective=profiles:~/.openclaw/agents/main/agent/auth-profiles.json | profiles=1 (oauth=0, token=0, api_key=1) | openai:default=sk-proj-...HDR0sZAA
  • openrouter effective=profiles:~/.openclaw/agents/main/agent/auth-profiles.json | profiles=1 (oauth=0, token=0, api_key=1) | openrouter:default=sk-or-v1...94cbac69

Missing auth

  • mets-llama openclaw configure or set an API key env var.

OAuth/token status

  • none
    p-gamer123@raspberrypi:~ $
sudden valeBOT
#

yep, that confirms it, your Default is now the one you wanted.

so OpenClaw should try that model first now.

one small cleanup thing though, your second fallback still looks typo’d:

mets-llama

That’s why it says missing auth for that one.

If you want, I can give you the exact command to clean that fallback out too.

onyx solstice
#

can u tell me how to fix?

sudden valeBOT
#

yep, easiest safe fix is:

openclaw configure --section model

then in the model config screen:

  • keep your default as it is
  • remove the bad fallback that starts with mets-llama
  • save and exit

then restart:

openclaw gateway restart
#

I’m recommending the interactive path here because it’s safer than trying to hand-edit one fallback entry blindly on your version.

onyx solstice
#

btw ais still running old model:

Hey — I’m Raspberry. I’m awake, I’m listening, and I’m ready. What’s the first thing on your mind?
whats ur model
I'm currently running on the OpenAI GPT-4o mini model. How can I assist you today?

sudden valeBOT
#

that reply is almost certainly not a reliable runtime check, it’s just text from your assistant prompt/persona or a stale instruction.

the trustworthy source is still:

openclaw models status

If you want to find where that GPT-4o mini line is coming from, run:

grep -Rni "GPT-4o mini" ~/openclaw ~/.openclaw 2>/dev/null

that should show you which file is telling your assistant to say that.

onyx solstice
#

so its running on the llama model

sudden valeBOT
#

yep, based on the openclaw models status output, your default is set to the Llama one now.

small caveat: if that model fails at runtime, OpenClaw can still fall back. But under normal conditions, yes, it should be using that one first.

onyx solstice
#

before it was showing 16 requests today and still showing 16 but the openrouter doesnt show any usage either

sudden valeBOT
#

that makes me think one of two things is happening:

  1. the request count page is delayed/stale
  2. your live session is still routing somewhere other than the provider you expect

fastest way to check is the gateway logs.

run this in one terminal:

openclaw logs --follow

then send one fresh message through your UI, and paste the new lines that show up right after.

that should tell us which provider/model OpenClaw is actually trying to use.
(just don’t paste any full secrets if they show up)

onyx solstice
#

ohhhhhh its cuz im using a free model

sudden valeBOT
#

yep, that’ll do it šŸ˜„

free-tier routes can be weird about usage counters / billing dashboards, so ā€œno usage shownā€ doesn’t necessarily mean it isn’t working.

if the responses are coming back and your config shows that as the default, you’re probably fine.

onyx solstice
#

wait but it says no requests

#

and openai dashboard updated. im still using gpt 4

sudden valeBOT
#

then yeah, don’t assume, let’s verify it properly.

if it really says no requests, there’s a decent chance your live traffic is still not hitting that provider.

run this:

openclaw logs --follow

then send one new test message through your UI and paste the new lines that appear.

that’ll tell us what OpenClaw is actually calling.

#

yep, if the OpenAI dashboard is moving, then your live requests are still ending up there.

the cleanest way to force your UI to use the Llama one is to pin it in the proxy request headers.

add this header inside your proxy fetch(...) headers:

"x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free",

so your headers block becomes:

headers: {
  "Content-Type": "application/json",
  "Authorization": `Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`,
#
  "x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free"
},

then restart the proxy.

that will hard-pin the UI requests to that model instead of relying on whatever session/default state is hanging around.

onyx solstice
#

i cant edit any code rn

sudden valeBOT
#

got it, then do the no-code version:

send this as a normal message in your UI:

/model openrouter/meta-llama/llama-3.3-70b-instruct:free

that should switch the current chat/session without you editing the proxy.

If that works, your next messages in that same session should use that model.

onyx solstice
#

says i dont have perms or smth and i want a switch in all chats

sudden valeBOT
#

yeah, that makes sense.

If you want the switch for all future chats, the command you already used is the right one:

openclaw models set openrouter/meta-llama/llama-3.3-70b-instruct:free
openclaw gateway restart

But here’s the annoying bit:

  • default model change applies to new sessions
  • existing chats/sessions can stay pinned to the old model
  • /model ... would switch an existing session, but your current UI/session doesn’t have permission for that

So without editing code, the practical answer is:

#
  1. keep the default changed globally
  2. start new chats / fresh sessions to pick it up
  3. old sessions may keep the previous model until they’re replaced

So the blocker isn’t the default anymore, it’s that your existing sessions are sticky.

onyx solstice
#

so new cloudflare?

sudden valeBOT
#

not Cloudflare itself, the important thing is a new session.

A new Cloudflare URL / new incognito tab can help if it causes your UI to start fresh, but the real goal is:

new browser session / new chat session

So, yeah, a new trycloudflare URL can be part of that, but it’s not the magical part by itself. The session reset is.

onyx solstice
#

still the old model

sudden valeBOT
#

then yeah, at this point something in that UI path is still pinning/reusing the old model/session.

So the honest answer is:

  • your global default changed
  • but that specific UI flow is still not obeying it
  • without editing the code, you probably won’t force that UI to switch

when you can edit code again, the proper fix is to hard-pin the model in the proxy request with:

"x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free"

That’s the clean fix for this specific UI.

onyx solstice
#

but i need the model changed now

sudden valeBOT
#

if you need it changed right now without editing your proxy/UI code, the fastest path is:

  1. open the built-in OpenClaw dashboard / web chat
  2. send:
/model openrouter/meta-llama/llama-3.3-70b-instruct:free

That should switch the built-in chat session immediately.

But for your custom Cloudflare UI specifically, I need to be blunt: if that path is still pinned to the old session/model, then without editing the code, I can’t force that exact UI to switch right now. The built-in chat is the no-code immediate workaround.

onyx solstice
#

ok tell me with ediitign the code

sudden valeBOT
#

replace your fetch(...) block with this exact one:

const upstream = await fetch("http://127.0.0.1:18789/v1/chat/completions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`,
    "x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free"
  },
  body: JSON.stringify(req.body)
});

then restart the proxy.

that header hard-pins your custom UI requests to that model instead of relying on sticky session/default state.

onyx solstice
#

mine is await fetch

sudden valeBOT
#

yep, that’s the one I mean šŸ˜„

find your await fetch( call and make the headers part look like this:

headers: {
  "Content-Type": "application/json",
  "Authorization": `Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`,
  "x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free"
}

you’re just adding that extra "x-openclaw-model" line inside the existing fetch headers.

onyx solstice
#

what do i do with this piece: messages: [
{ role: "system", content: "Your name is Raspberry. Do not change y>
{ role: "user", content: text }

sudden valeBOT
#

for the model switch, you don’t need to change that part.

that messages block is just the chat prompt, like:

  • who the assistant says it is
  • what tone/persona it uses

it does not choose the backend model.

So for changing the model, leave that alone and only add:

"x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free"

inside the headers of your fetch(...).

#

If you want, paste the full await fetch(...) block and I’ll show you exactly where that line goes.

onyx solstice
#

now the web ui just hangs

sudden valeBOT
#

okay, let’s not guess.

fastest check, run this again locally:

curl -i http://127.0.0.1:8081/api \
  -H "Content-Type: application/json" \
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'

and also look at the proxy terminal for any new error.

paste:

  1. the curl output
  2. anything new the proxy terminal prints

that’ll tell us whether the new header broke the request or the UI is just waiting forever.

onyx solstice
#

usually happens when theres a typo in the code or smth

sudden valeBOT
#

yep, that’s very possible.

paste your current full await fetch(...) block exactly as it is now.

That’s the quickest way for me to spot the typo instead of making you play whack-a-mole.

onyx solstice
#

try {
const res = await fetch("/api", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
"x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:fre>
model: "openclaw",
messages: [
{ role: "system", content: "Your name is Raspberry. Do not change y>
{ role: "user", content: text }
]
})
});

sudden valeBOT
#

found it.

you put "x-openclaw-model" inside the JSON body, but it needs to go in the HTTP headers.

replace your fetch block with this:

try {
  const res = await fetch("/api", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free"
    },
    body: JSON.stringify({
      model: "openclaw",
#
      messages: [
        { role: "system", content: "Your name is Raspberry. Do not change your name." },
        { role: "user", content: text }
      ]
    })
  });

So:

  • remove "x-openclaw-model" from inside body: JSON.stringify({...})
  • add it inside headers

That’s why it was hanging / acting weird.

onyx solstice
#

same issue

sudden valeBOT
#

ah, okay, then I see the real problem.

you added it to the browser fetch, but your setup has a proxy server in the middle, so the header needs to be added in the proxy’s fetch to OpenClaw, not just in the UI.

in your proxy code, find this part:

const upstream = await fetch("http://127.0.0.1:18789/v1/chat/completions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`
  },
  body: JSON.stringify(req.body)
});
#

and change it to this:

const upstream = await fetch("http://127.0.0.1:18789/v1/chat/completions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`,
    "x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free"
  },
  body: JSON.stringify(req.body)
});

So:

  • UI fetch can stay normal
  • proxy fetch to OpenClaw is where the model-pin header must go
#

That’s the part that actually talks to OpenClaw.

onyx solstice
#

/home/p-gamer123/raspberry-chat/proxy.js:19
"x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free"
^^^^^^^^^^^^^^^^^^

SyntaxError: Unexpected string
at wrapSafe (node:internal/modules/cjs/loader:1637:18)
at Module._compile (node:internal/modules/cjs/loader:1679:20)
at Object..js (node:internal/modules/cjs/loader:1838:10)
at Module.load (node:internal/modules/cjs/loader:1441:32)
at Function._load (node:internal/modules/cjs/loader:1263:12)
at TracingChannel.traceSync (node:diagnostics_channel:328:14)
at wrapModuleLoad (node:internal/modules/cjs/loader:237:24)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:171:5)
at node:internal/main/run_main_module:36:49

Node.js v22.22.1
p-gamer123@raspberrypi:~/raspberry-chat $

sudden valeBOT
#

yep, that means there’s almost certainly a missing comma right above it.

use this exact headers block:

headers: {
  "Content-Type": "application/json",
  "Authorization": `Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`,
  "x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free"
},

The important bit is the comma after the Authorization line. Without that, JavaScript sees the next string and throws Unexpected string.

onyx solstice
#

done but still hanging

sudden valeBOT
#

okay, then let’s test the pinned model directly, without the UI and without the proxy confusion.

run this exact command:

curl -i http://127.0.0.1:18789/v1/chat/completions \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free" \
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'

If that hangs too, the issue is the model/provider path itself, not your UI.

If it returns normally, then the issue is still in the proxy/UI layer.

Paste what happens.

onyx solstice
#

/completions
-H "Authorization: Bearer $TOKEN"
-H "Content-Type: application/json"
-H "x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free"
-d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'
HTTP/1.1 401 Unauthorized
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Type: application/json; charset=utf-8
Date: Mon, 20 Apr 2026 11:37:34 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 58

{"error":{"message":"Unauthorized","type":"unauthorized"}}curl: (6) Could not resolve host: openrouter
p-gamer123@raspberrypi:~/raspberry-chat $

sudden valeBOT
#

got it, two things went wrong there:

  1. your x-openclaw-model header was quoted wrong
  2. 401 Unauthorized means $TOKEN is probably blank/stale in that shell

use this exact command instead, in a shell where $TOKEN is actually set:

curl -i http://127.0.0.1:18789/v1/chat/completions \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "x-openclaw-model: openrouter/meta-llama/llama-3.3-70b-instruct:free" \
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'

If you’re not sure $TOKEN is set in this shell, say that and I’ll give you the fresh-token version again.

onyx solstice
#

same error

sudden valeBOT
#

yep, then let’s stop relying on whatever $TOKEN used to be and make a fresh one in this same shell.

run this exact block:

TOKEN="$(openssl rand -hex 32)"
openclaw config set gateway.auth.token "$TOKEN"
openclaw gateway restart
curl -i http://127.0.0.1:18789/v1/chat/completions \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "x-openclaw-model: openrouter/meta-llama/llama-3.3-70b-instruct:free" \
  -d '{"model":"openclaw/default","messages":[{"role":"user","content":"hi"}]}'

that removes the token mismatch entirely, because the gateway and the curl will both use the same fresh token from the same terminal.

onyx solstice
#

same

sudden valeBOT
#

I need the exact output now, not just ā€œsameā€.

paste the full result from the terminal after running it, even if it looks ugly.

Right now I can’t tell whether ā€œsameā€ means:

  • 401 Unauthorized
  • Could not resolve host
  • syntax error
  • or something else

paste the exact text and I’ll tell you the exact fix.

onyx solstice
#

can u jst tekll me how to change the model. the ui freezing like this means that theres a typo/syntax error nin the cde

sudden valeBOT
#

yep, keep it simple.

to change the model, only edit this one part in proxy.js:

headers: {
  "Content-Type": "application/json",
  "Authorization": `Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`,
  "x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free"
},

That goes inside the server-side fetch("http://127.0.0.1:18789/v1/chat/completions", { ... }) block in your proxy.

So the full fetch should look like this:

#
const upstream = await fetch("http://127.0.0.1:18789/v1/chat/completions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`,
    "x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free"
  },
  body: JSON.stringify(req.body)
});

That is the model-change part. Nothing else is required for the switch itself.

onyx solstice
#

still not working maybe u errored in the ui

#

still not working i think u may have made an error in the ui

#

@sudden vale

#

@sudden vale

sudden valeBOT
#

yep, I’m here.

#

yep?

onyx solstice
#

its still not working i think u might have errored in teh ui

sudden valeBOT
#

yeah, I think the safest move is to reset the UI part to the known-good version and stop touching the model there.

replace your current request/response block with this exact chunk:

try {
  const res = await fetch("/api", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      model: "openclaw",
      messages: [
        { role: "system", content: "Your name is Raspberry. Do not change your name." },
        { role: "user", content: text }
#
      ]
    })
  });

  removeTyping();

  if (!res.ok) {
    addBubble("ai", "Gateway error: " + res.status);
    sendBtn.disabled = false;
    return;
  }

  const data = await res.json();
  const reply =
    data.choices?.[0]?.message?.content ||
#
    data.reply ||
    data.response ||
    data.message ||
    JSON.stringify(data);

  addBubble("ai", reply);

} catch (err) {
  removeTyping();
  addBubble("ai", "Failed to reach gateway.");
}

That is the UI-side block only.

Important:

#
  • no x-openclaw-model in the UI
  • model pinning belongs only in the proxy fetch to OpenClaw

This should get the UI back to a clean working state.

onyx solstice
#

tell me the code in one msg

sudden valeBOT
#
try {
  const res = await fetch("/api", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      model: "openclaw",
      messages: [
        { role: "system", content: "Your name is Raspberry. Do not change your name." },
        { role: "user", content: text }
      ]
    })
  });
#
  removeTyping();

  if (!res.ok) {
    addBubble("ai", "Gateway error: " + res.status);
    sendBtn.disabled = false;
    return;
  }

  const data = await res.json();
  const reply =
    data.choices?.[0]?.message?.content ||
    data.reply ||
    data.response ||
    data.message ||
    JSON.stringify(data);
#

  addBubble("ai", reply);

} catch (err) {
  removeTyping();
  addBubble("ai", "Failed to reach gateway.");
}
onyx solstice
#

try { const res = await fetch("/api", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ model: "openclaw", messages: [ { role: "system", content: "Your name is Raspberry. Do not change your name." }, { role: "user", content: text } ] }) }); removeTyping(); if (!res.ok) { addBubble("ai", "Gateway error: " + res.status); sendBtn.disabled = false; return; } const data = await res.json(); const reply = data.choices?.[0]?.message?.content || data.reply || data.response || data.message || JSON.stringify(data); addBubble("ai", reply); } catch (err) { removeTyping(); addBubble("ai", "Failed to reach gateway."); }

sudden valeBOT
#

yep, that UI block looks fine now.

so the UI side is back to normal.

the only place left for the model pin is the proxy fetch to OpenClaw. If you want, paste that proxy fetch("http://127.0.0.1:18789/v1/chat/completions", ...) block next and I’ll check the model header there.

onyx solstice
#

try {
const res = await fetch("/api", {
method: "POST",
headers: { "Content-Type": "application/json" },
model: "openclaw",
body: JSON.stringify({
messages: [
{ role: "system", content: "Your name is Raspberry. Do not change y>
{ role: "user", content: text }
]
})

this good? will the model be changed now

sudden valeBOT
#

no, that one is not good yet.

two problems:

  1. model: "openclaw" is in the wrong place
  2. this is still the UI fetch, so it still will not change the backend model by itself

the correct UI block is:

try {
  const res = await fetch("/api", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      model: "openclaw",
#
      messages: [
        { role: "system", content: "Your name is Raspberry. Do not change your name." },
        { role: "user", content: text }
      ]
    })
  });

That fixes the UI syntax, but no, that alone will not pin the model.

The actual model change still has to happen in the proxy fetch to OpenClaw, not in this UI block.

onyx solstice
#

it gives me 401 now

sudden valeBOT
#

yep, 401 means the proxy token and gateway token don’t match again.

most likely you restarted node proxy.js in a shell where the token env wasn’t set.

run this in the same terminal where you start the proxy:

Ctrl+C
TOKEN="$(openssl rand -hex 32)"
openclaw config set gateway.auth.token "$TOKEN"
openclaw gateway restart
export OPENCLAW_GATEWAY_TOKEN="$TOKEN"
node proxy.js

that re-syncs the gateway + proxy token again.

#

401 is auth mismatch, not the model change itself.

onyx solstice
#

now says āš ļø Agent couldn't generate a response. Please try again.

sudden valeBOT
#

that message is too generic by itself, so the next step is to catch the real error in the gateway logs.

run this in a terminal:

openclaw logs --follow

then send one test message through your UI and paste the new lines that appear right after.

that’ll show whether it’s:

  • auth
  • model/provider failure
  • bad request format
  • or something else.
onyx solstice
#

09:16:16+00:00 info gateway/channels/discord {"subsystem":"gateway/channels/discord"} discord client initialized as 1480508013600182384 (OpenClawAI); awaiting gateway readiness
09:16:16+00:00 info gateway/channels/discord {"subsystem":"gateway/channels/discord"} discord gateway: Gateway websocket closed: 4014
09:16:16+00:00 error gateway/channels/discord {"subsystem":"gateway/channels/discord"} discord: gateway closed with code 4014 (missing privileged gateway intents). Enable the required intents in the Discord Developer Portal or disable them in config.
09:23:45+00:00 warn agent/embedded {"subsystem":"agent/embedded"} incomplete turn detected: runId=chatcmpl_ffe2c303-884e-4edd-929b-fbae581b5215 sessionId=60e77edc-23e7-4d93-b3cc-7b525d0c53da stopReason=stop payloads=0 — surfacing error to user
09:23:45+00:00 info āš ļø Agent couldn't generate a response. Please try again.
09:23:54+00:00 warn agent/embedded {"subsystem":"agent/embedded"} incomplete turn detected: runId=chatcmpl_b45e6f4e-cf9b-4b6a-b885-ac3f1d2c3b50 sessionId=b690cecb-d7cf-46b5-a139-cabe5d0b823f stopReason=stop payloads=0 — surfacing error to user
09:23:55+00:00 info āš ļø Agent couldn't generate a response. Please try again.
Log tail truncated (increase --max-bytes).
09:25:34+00:00 warn agent/embedded {"subsystem":"agent/embedded"} incomplete turn detected: runId=chatcmpl_fd0e840a-196e-4233-a17d-d7e0a9cbed98 sessionId=c983b28c-4186-4bb7-b1ec-f086254499ca stopReason=stop payloads=0 — surfacing error to user
09:25:35+00:00 info āš ļø Agent couldn't generate a response. Please try again.

sudden valeBOT
#

good, the Discord 4014 bit is unrelated to your Cloudflared UI. you can ignore that for this problem.

the real clue is this:

incomplete turn detected ... payloads=0

which means the model path stopped without returning usable output.

the tail you pasted is truncated, so we need a bigger slice.

run this:

openclaw logs --follow --max-bytes 200000
#

then send one test message through the UI and paste the lines from just before the incomplete turn detected warning.

that should show the actual provider/model error right before it dies.

onyx solstice
#

=09:16:14+00:00 warn gateway/channels/discord {"subsystem":"gateway/channels/discord"} [default] Discord Message Content Intent is disabled; bot may not respond to channel messages. Enable it in Discord Dev Portal (Bot → Privileged Gateway Intents) or require mentions.
09:16:14+00:00 info gateway/channels/discord {"subsystem":"gateway/channels/discord"} [default] starting provider (@OpenClawAI)
09:16:16+00:00 info gateway/channels/discord {"subsystem":"gateway/channels/discord"} discord client initialized as 1480508013600182384 (OpenClawAI); awaiting gateway readiness
09:16:16+00:00 info gateway/channels/discord {"subsystem":"gateway/channels/discord"} discord gateway: Gateway websocket closed: 4014
09:16:16+00:00 error gateway/channels/discord {"subsystem":"gateway/channels/discord"} discord: gateway closed with code 4014 (missing privileged gateway intents). Enable the required intents in the Discord Developer Portal or disable them in config.
09:23:45+00:00 warn agent/embedded {"subsystem":"agent/embedded"} incomplete turn detected: runId=chatcmpl_ffe2c303-884e-4edd-929b-fbae581b5215 sessionId=60e77edc-23e7-4d93-b3cc-7b525d0c53da stopReason=stop payloads=0 — surfacing error to user
09:23:45+00:00 info āš ļø Agent couldn't generate a response. Please try again.
09:23:54+00:00 warn agent/embedded {"subsystem":"agent/embedded"} incomplete turn detected: runId=chatcmpl_b45e6f4e-cf9b-4b6a-b885-ac3f1d2c3b50 sessionId=b690cecb-d7cf-46b5-a139-cabe5d0b823f stopReason=stop payloads=0 — surfacing error to user
09:23:55+00:00 info āš ļø Agent couldn't generate a response. Please try again.
09:25:34+00:00 warn agent/embedded {"subsystem":"agent/embedded"} incomplete turn detected: runId=chatcmpl_fd0e840a-196e-4233-a17d-d7e0a9cbed98 sessionId=c983b28c-4186-4bb7-b1ec-f086254499ca stopReason=stop payloads=0 — surfacing error to user
09:25:35+00:00 info āš ļø Agent couldn't generate a response. Please try again.

sudden valeBOT
#

those logs still aren’t giving the provider’s internal error, they’re only telling us:

the run stopped and produced 0 usable output

so at this point, the practical read is:

  • the Discord 4014 stuff is unrelated
  • your forced Llama/free route is very likely the thing failing silently
  • OpenClaw is surfacing that as ā€œcouldn’t generate a responseā€

If you need it working now, my recommendation is:

  1. remove the forced model pin from the proxy
  2. go back to the last known-working model/default
  3. restart the proxy + gateway
#

If you want, I can give you the exact one-block code to remove the x-openclaw-model line cleanly and get you back to a working state first.

onyx solstice
#

now how do i change model

sudden valeBOT
#

you’ve got two ways to change it:

1) change the default model for OpenClaw

openclaw models set <provider/model>
openclaw gateway restart

That changes the default model OpenClaw should use.

2) force your custom UI/proxy to use a specific model

in the proxy’s server-side fetch to OpenClaw, add:

"x-openclaw-model": "<provider/model>"
#

inside the headers block.


short version:

  • global default → openclaw models set ...
  • your custom UI specifically → x-openclaw-model in the proxy

If you want, tell me the exact model you want now, and I’ll send the exact line/command only.

onyx solstice
#

i want model: "meta-llama/llama-3.3-70b-instruct:free"

sudden valeBOT
#

for your setup, use the OpenRouter-prefixed form:

openrouter/meta-llama/llama-3.3-70b-instruct:free

global default

openclaw models set openrouter/meta-llama/llama-3.3-70b-instruct:free
openclaw gateway restart

proxy header

"x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free"
#

important bit: in your setup it’s not just meta-llama/..., it needs the openrouter/ prefix.

onyx solstice
#

u forgot the comma btw do i do it under the authorisation stuffy

sudden valeBOT
#

yep, good catch, and yes, put it under the Authorization line inside the same headers object.

use this exact block:

headers: {
  "Content-Type": "application/json",
  "Authorization": `Bearer ${process.env.OPENCLAW_GATEWAY_TOKEN}`,
  "x-openclaw-model": "openrouter/meta-llama/llama-3.3-70b-instruct:free"
},

So:

  • same headers
  • same place as Authorization
  • with a comma after the Authorization line
onyx solstice
#

then it should work?

sudden valeBOT
#

it should force your proxy requests to try that model, yes.

So if:

  • the token is correct
  • the proxy is restarted
  • that model route is actually healthy

then yes, it should work.

small caveat: if that specific free route is flaky again, it can still fail even though the header is correct. So the header fixes the routing, not the model’s own reliability.

onyx solstice
#

nope still agent couldnt generate a response. i think the codes wrong. or do u think i havent set up openrouter properly?

sudden valeBOT
#

I don’t think your general OpenRouter setup is the problem, because earlier your auth/status output showed an OpenRouter API key was present.

so the more likely situation is:

  • your code path is basically okay
  • but that specific free Llama route is the part failing

easy way to prove that:

change just this line to:

"x-openclaw-model": "openrouter/auto"

If openrouter/auto works, then:

  • your proxy code is fine
#
  • your OpenRouter auth is fine
  • that specific meta-llama/...:free route is the thing breaking

If you want the fastest test, that’s the one I’d do next.

onyx solstice
#

/auto gives gateway error:400

sudden valeBOT
#

ah, that’s actually a useful clue.

In your status output, the default showed up as:

openrouter/openrouter/auto

so try the fully resolved form, not the short one.

use:

"x-openclaw-model": "openrouter/openrouter/auto"

instead of:

#
"x-openclaw-model": "openrouter/auto"

That double openrouter/... looks weird, but your status output suggests that’s how your build is resolving it.

onyx solstice
#

still 400

sudden valeBOT
#

okay, at this point I need the actual 400 body, not just the status code.

run this and paste the full output:

curl -i http://127.0.0.1:8081/api \
  -H "Content-Type: application/json" \
  -d '{"model":"openclaw","messages":[{"role":"user","content":"hi"}]}'

the reason is simple: 400 could mean:

  • bad model override format
  • unsupported header in your build
  • malformed request body
  • provider rejection

the body text will tell us which one.

onyx solstice
#

openrouter/auto is not allowed for agent main

sudden valeBOT
#

perfect, that explains the 400.

it means:

  • your code/header format is okay enough to be parsed
  • but openrouter/auto is not in the allowed model list for agent main

So:

  • openrouter/auto → rejected by allowlist
  • openrouter/meta-llama/llama-3.3-70b-instruct:free → allowed, but that route seems to be the one failing to generate output

So this is not just a typo now.

If you want openrouter/auto, you need to add it to the configured/allowed models in your model config.

If you want to keep using the Llama free route, then the header is the right idea, but the problem is likely that that specific route is flaky / returning nothing.

onyx solstice
#

so how do i fix it?

sudden valeBOT
#

you’ve basically got two fixes now:

easiest fix, recommended

stop forcing the flaky free route.

  • remove the x-openclaw-model line from the proxy
  • use your normal default model again

if you specifically want openrouter/auto

you need to add it to the agent’s allowed models.

do:

openclaw configure --section model
#

then:

  • add openrouter/auto to the configured/allowed models
  • save
  • restart the gateway

after that, you can either:

  • leave the proxy with no model pin and let the default handle it
  • or pin to:
    "x-openclaw-model": "openrouter/auto"
    

important reality check

your meta-llama/...:free route looks allowed but unstable, so there may be nothing to ā€œfixā€ there besides choosing a more reliable route.

If you want, I can give you the exact one-block code to remove the model pin and get you back to working first.

onyx solstice
#

i want a free meta llama model running

sudden valeBOT
#

got it, but the important bit is:

you don’t just want ā€œa Meta Llama modelā€, you want a free Meta Llama route that actually works.

right now, the one you pinned looks like it’s allowed but not reliably returning output in your setup.

So the practical fix is:

  1. run
    openclaw configure --section model
    
  2. pick a different free Meta Llama route if one is available there
  3. save
  4. restart the gateway

If you want stable free Meta Llama, local/self-hosted is usually more predictable than random free hosted routes.

onyx solstice
#

how do ichange openrouter/auto to a diff model

sudden valeBOT
#

if you want to replace openrouter/auto as the default, do:

openclaw models set <provider/model>
openclaw gateway restart

for example, with the one you were trying earlier:

openclaw models set openrouter/meta-llama/llama-3.3-70b-instruct:free
openclaw gateway restart

and if your proxy code still has:

#
"x-openclaw-model": "..."

remember that header will override the default, so either:

  • change that header too
  • or remove it if you want the default model to take over.
onyx solstice
#

i just want a free llama model. ok?

sudden valeBOT
#

yep, totally fair.

then keep it simple:

  1. run
    openclaw configure --section model
    
  2. pick a free Llama route
  3. save
  4. restart
    openclaw gateway restart
    

and if your proxy still has an x-openclaw-model header, either:

  • remove it, so the new default is used
#
  • or set it to the exact free Llama route you picked

That’s the clean path. No auto, no weird override guessing.

onyx solstice
#

what model will openrouter/auto give me

sudden valeBOT
#

there isn’t one fixed answer.

openrouter/auto can route you to different backend models dynamically, so it’s not a stable ā€œthis always equals Xā€ setting.

If you want a guaranteed specific model, don’t use auto, pick a specific provider/model instead.

For current real-world behavior, best bet is:

  • send a test request
  • check your provider usage/logs
  • ask in #models for fresh community experience

Docs:

onyx solstice
#

k its giving me agent failed to generate a response what doi do

sudden valeBOT
#

Copilot is almost certainly hallucinating here — there is no separate ā€œgateway binaryā€ you need from the creator. The Gateway ships with openclaw.

If you’re now seeing ā€œagent failed/couldn’t generate a responseā€, the tunnel is probably no longer the main issue; that usually means the Gateway received the message but the agent/model run failed.

Run these on the machine running OpenClaw:

openclaw --version
openclaw status --all
openclaw models status
openclaw infer model run --prompt "Reply exactly: smoke-ok" --json
openclaw logs --follow

Also, for the Cloudflare part:

#
curl -i http://127.0.0.1:18789/healthz
curl -i https://YOUR-CLOUDFLARE-DOMAIN/healthz

Expected: JSON like:

{"ok":true,"status":"live"}

If / returns HTML, that’s normal — the Gateway serves the Control UI there. If /healthz through Cloudflare gives a Cloudflare 404, your tunnel ingress is pointing at the wrong place; it should route to the local Gateway, usually http://127.0.0.1:18789.

If your browser is using a Cloudflare domain, you may also need the exact origin allowed:

onyx solstice
#

so how do i fix it? auth issues give the code 401 or 400 but this isnt that so it must be model issues

sudden valeBOT
#

You’re mixing two different layers:

  • Cloudflare 404 = tunnel/ingress/path problem. The request is probably not reaching OpenClaw.
  • ā€œAgent failed to generate a responseā€ = OpenClaw reached the agent, but the model/runtime failed.

Fix in this order:

openclaw --version
openclaw gateway status
curl -i http://127.0.0.1:18789/healthz

/ returning HTML is normal. Test /healthz; it should return:

#
{"ok":true,"status":"live"}

Then test Cloudflare:

curl -i https://YOUR-DOMAIN/healthz

If local /healthz works but Cloudflare /healthz is 404, fix your cloudflared ingress. It should look roughly like:

ingress:
  - hostname: YOUR-DOMAIN
    service: http://127.0.0.1:18789
#
  - service: http_status:404

Then restart cloudflared.

For the browser/dashboard origin, also set:

openclaw config set gateway.controlUi.allowedOrigins '["https://YOUR-DOMAIN"]' --strict-json
openclaw gateway restart

After that, test the model separately:

onyx solstice
#

works

sudden valeBOT
#

Nice, glad it’s working šŸŽ‰

If that fixed it, please right-click the solution message → Apps → āœ… Mark Solution so Answer Overflow indexes it for future folks. 🦐

onyx solstice
#

but can u tell me whats the issue with the model?

sudden valeBOT
#

Yep — the ā€œmodel issueā€ was basically:

  • OpenClaw reached the agent.
  • The selected OpenRouter/free Llama route ended with 0 usable output.
  • Logs showed incomplete turn detected ... payloads=0, so OpenClaw surfaced: ā€œAgent couldn’t generate a response.ā€

So it wasn’t Cloudflared at that point, and probably not your gateway token. It was the model route/pin being unreliable or not returning a valid response.

Also earlier: openrouter/auto is not allowed for agent main means your model allowlist rejected openrouter/auto. That’s a config/allowlist issue, separate from the free Llama route failing.

Fast proof:

openclaw models status
openclaw infer model run --prompt "Reply exactly: smoke-ok" --json
#

If another model works but that free route doesn’t, the route is the problem. For fresh route recommendations, ask in #models since free OpenRouter routes change a lot.

Docs: https://docs.openclaw.ai/concepts/models

onyx solstice
#

is it safe to turn off my raspberrypi if its realllllly hot and its just displaying a grey screen

sudden valeBOT
#

Yes — if it’s really hot and stuck on a grey screen, turn it off.

Best order:

  1. If you can SSH in, run:
    sudo shutdown now
    
  2. If it’s frozen/no SSH, unplug power. There’s a small SD-card corruption risk, but overheating is the bigger concern.
  3. Let it cool completely before powering back on.
  4. Check airflow, case, heatsink/fan, and power supply before retrying.

Don’t keep it running hot just to preserve OpenClaw. Once it’s cooled down, we can debug the Cloudflared/OpenClaw part safely.

onyx solstice
#

its not doing anything on screen

sudden valeBOT
#

Then don’t wait on the screen — power it off now.

If it’s unresponsive and very hot:

  1. Unplug the Raspberry Pi power cable.
  2. Leave it off for 10–15 minutes to cool.
  3. Don’t touch the board/heatsink if it’s too hot.
  4. Before powering back on, make sure it has airflow / fan / heatsink and isn’t enclosed somewhere hot.

There’s some SD-card risk from a hard power cut, but a frozen overheating Pi is the bigger problem.

onyx solstice
#

the fan keeps changing speeds so its running smth what do i do

sudden valeBOT
#

The fan changing speed just means the Pi/fan controller still has power — it doesn’t mean it’s healthy.

Do this:

  1. Try SSH once:
    ssh pi@<your-pi-ip>
    sudo shutdown now
    
  2. If SSH doesn’t connect quickly, unplug the power.
  3. Leave it off until it’s fully cool.

Don’t keep waiting because ā€œsomething is runningā€ — if it’s very hot + grey screen + no response, safe shutdown probably isn’t happening anyway. Hard power-off is the right call here.

onyx solstice
#

gateway error 500

sudden valeBOT
#

If the Pi was overheating, first make sure it’s cooled down before debugging.

A 500 gateway error means something server-side is failing now — either OpenClaw crashed/errored, or Cloudflare is reaching the wrong thing. Don’t guess from the browser page; check local first:

openclaw --version
openclaw gateway status
curl -i http://127.0.0.1:18789/healthz
openclaw status --all

Then check logs:

openclaw logs --follow
#

Rule of thumb:

  • local /healthz fails → OpenClaw/gateway issue
  • local /healthz works but Cloudflare URL gives 500 → Cloudflared tunnel/ingress issue
  • chat/API request gives 500 but /healthz works → likely model/runtime error; logs will say why

Paste openclaw --version, openclaw gateway status, and the /healthz curl output.

Docs: https://docs.openclaw.ai/gateway/troubleshooting

onyx solstice
#

i feel like its the gateway

sudden valeBOT
#

It could be the gateway, yeah — but we can split it cleanly.

Run these on the Raspberry Pi:

openclaw --version
openclaw gateway status
curl -i http://127.0.0.1:18789/healthz
openclaw logs --follow

If /healthz does not return something like:

{"ok":true,"status":"live"}
#

then yes, it’s a local OpenClaw gateway problem.

If local /healthz works but your Cloudflare URL fails, then the gateway is fine and Cloudflared is pointing at the wrong host/port/path.

Also: there is no separate magic ā€œgateway binaryā€ you need from the creator — it’s part of OpenClaw.

Docs: https://docs.openclaw.ai/gateway/troubleshooting

onyx solstice
#

the ui works the proxy works (I can tell cuz the proxy feeds a message from raspberry to the ui thats not from the gateway) and it says http/1.1 200 ok

sudden valeBOT
#

That means the web/UI layer is alive, but it does not prove the OpenClaw gateway connection is alive.

Important distinction:

  • HTTP/1.1 200 OK from / usually just means ā€œthe UI HTML loadedā€
  • the actual gateway health check is:
    curl -i http://127.0.0.1:18789/healthz
    
  • and through Cloudflare:
    curl -i https://YOUR-DOMAIN/healthz
    

If your proxy is feeding a Raspberry Pi message to the UI not from the gateway, then your UI/proxy may be working while the UI’s backend/WebSocket/API route to OpenClaw is broken.

Check the browser dev console/network tab for failed requests, especially WebSocket/connect calls. Also run:

#
openclaw gateway status
openclaw status --all
openclaw logs --follow

The useful test is: does /healthz return JSON locally and through the domain? If yes, gateway is reachable; then the remaining issue is probably auth/model/runtime, not Cloudflared.

onyx solstice
#

no json from /healthz

sudden valeBOT
#

Then yes — that’s the key problem.

If /healthz does not return JSON, either:

  1. OpenClaw gateway is not running, or
  2. you’re curling the wrong port/service, or
  3. your proxy is serving the UI fallback HTML instead of forwarding /healthz to OpenClaw.

Run this on the Pi:

openclaw --version
openclaw gateway status
openclaw status --all
curl -i http://127.0.0.1:18789/healthz
#

If openclaw gateway status shows a different probe URL/port, curl that exact URL instead.

Also check what owns the port:

ss -ltnp | grep 18789

If / gives HTML but /healthz doesn’t give JSON, your browser UI can load, but the actual gateway route is not being reached. Don’t debug the model yet — fix /healthz first.

onyx solstice
#

k how do i fix

sudden valeBOT
#

Do this in order, on the Pi:

openclaw gateway restart
openclaw gateway status
curl -i http://127.0.0.1:18789/healthz

If /healthz still isn’t JSON:

ss -ltnp | grep 18789
openclaw logs --follow

What to fix based on result:

#
  • Nothing listening on 18789 → gateway isn’t running:
    openclaw gateway start
    
  • Something else is on 18789 → your proxy/UI is occupying the OpenClaw port. Stop that or move it; OpenClaw gateway needs 18789.
  • Gateway runs on a different port → use the URL shown by openclaw gateway status.
  • Local /healthz works, Cloudflare /healthz doesn’t → fix cloudflared ingress to point directly at OpenClaw:
    ingress:
      - hostname: YOUR-DOMAIN
        service: http://127.0.0.1:18789
      - service: http_status:404
    

For now, bypass your custom proxy entirely. First make http://127.0.0.1:18789/healthz return JSON locally. Then put Cloudflared in front of it.

Docs: https://docs.openclaw.ai/gateway/troubleshooting

onyx solstice
#

cannot get heathz when i run it thru cloudflared but grep says that openclaw is running on 18789

sudden valeBOT
#

That points to Cloudflared ingress, not OpenClaw.

First: make sure the path is spelled exactly /healthz — not /heathz.

Run:

curl -i http://127.0.0.1:18789/healthz
curl -i https://YOUR-DOMAIN/healthz
cloudflared tunnel ingress validate
cloudflared tunnel ingress rule https://YOUR-DOMAIN/healthz

Cloudflare’s own docs say ingress rules are matched top-to-bottom, and cloudflared tunnel ingress rule <url> shows which rule matched. If it matches your catch-all http_status:404, your hostname/path rule is wrong. (developers.cloudflare.com)

Your tunnel config should be basically:

Cloudflare Docs

Configure locally-managed tunnels with a YAML configuration file.

#
ingress:
  - hostname: YOUR-DOMAIN
    service: http://127.0.0.1:18789
  - service: http_status:404

Then restart cloudflared. If local /healthz works but domain /healthz doesn’t, don’t touch models/gateway yet — the tunnel is not forwarding that request to OpenClaw.

onyx solstice
#

but cloudflared is working its the ui forwarding to the gate thats not

sudden valeBOT
#

Right — then the broken part is your UI/proxy routing to OpenClaw, not Cloudflared itself.

Most common mistake: the browser/UI tries to call:

http://127.0.0.1:18789

from the user’s browser. That points to the user’s computer, not the Raspberry Pi.

Fix options:

Best/simple: point Cloudflared directly at OpenClaw and skip the custom UI proxy:

ingress:
#
  - hostname: YOUR-DOMAIN
    service: http://127.0.0.1:18789
  - service: http_status:404

If keeping your own UI/proxy: the proxy must forward requests server-side to:

http://127.0.0.1:18789

and it must support WebSocket upgrade, not just normal HTTP.

Test from the Pi:

#
curl -i http://127.0.0.1:18789/healthz

Then test through your UI/proxy route:

curl -i https://YOUR-DOMAIN/healthz

If first works and second doesn’t, your UI/proxy is not forwarding /healthz/gateway routes to OpenClaw correctly. Docs: https://docs.openclaw.ai/web/dashboard

onyx solstice
#

but i havent changed anything... do u think the files got corrupted?

sudden valeBOT
#

Possible, but I wouldn’t assume corruption first.

If the Pi overheated/froze, SD-card corruption is possible. But if OpenClaw is still running on 18789, this is more likely routing/proxy/service state.

Run these:

openclaw --version
curl -i http://127.0.0.1:18789/healthz
openclaw gateway status
openclaw logs --follow

Then check for SD/filesystem problems:

#
dmesg -T | grep -Ei "i/o error|ext4|mmc|corrupt|read-only"
df -h

Interpretation:

  • local /healthz returns JSON → OpenClaw files are probably fine; fix proxy/cloudflared route.
  • local /healthz fails + logs show OpenClaw errors → gateway/config issue.
  • dmesg shows I/O error, mmc, EXT4-fs error, or filesystem went read-only → yeah, likely SD-card/filesystem damage. Shut down, back up what you can, reflash/restore.

Don’t run repair commands blindly while the system is mounted. First paste the outputs above.

onyx solstice
#

but why would anything else be wrong cuz i only shut it down without changing any files

sudden valeBOT
#

Because a shutdown can change state even if you didn’t edit files.

A few realistic causes:

  • If you unplugged it while services were writing logs/cache/config, the SD card could have a small bad write.
  • Services can restart in a different order after boot — OpenClaw, your UI, and cloudflared may not all be ready at the same time.
  • The custom UI/proxy may have started, but OpenClaw may not have started correctly.
  • The IP/port target may still exist, but the route being forwarded is different.
  • A hot Pi can throttle/freeze and leave services half-started.

That said, don’t panic — this is probably not ā€œeverything corrupted.ā€

The truth test is still:

curl -i http://127.0.0.1:18789/healthz
#

If that works locally, OpenClaw is fine and only the forwarding/proxy path is wrong.

If it doesn’t, run:

openclaw gateway restart
openclaw gateway status
openclaw logs --follow

Then we’ll know instead of guessing.

onyx solstice
#

healthz says json like ok true status live

sudden valeBOT
#

Nice — that means OpenClaw gateway is healthy. Not corrupted, not missing a gateway binary. 🦐

So the broken bit is between Cloudflared/custom UI/proxy and OpenClaw.

Next test both paths:

curl -i http://127.0.0.1:18789/healthz
curl -i https://YOUR-DOMAIN/healthz

If local is JSON but domain is not, fix Cloudflared/proxy routing.

Recommended simple config:

#
ingress:
  - hostname: YOUR-DOMAIN
    service: http://127.0.0.1:18789
  - service: http_status:404

If you’re using a custom UI proxy instead of sending Cloudflared directly to OpenClaw, it must forward /healthz and the gateway WebSocket/API routes to http://127.0.0.1:18789. If it only serves the UI HTML, you’ll keep getting HTML/404/500 instead of gateway JSON.

onyx solstice
#

yaml isnt a valid cmd

sudden valeBOT
#

Yep — sorry, that block was not a command. It’s the contents that go inside your cloudflared config file.

Usually the file is one of these:

~/.cloudflared/config.yml
/etc/cloudflared/config.yml

Open it with an editor, for example:

nano ~/.cloudflared/config.yml

Then make the ingress: section look like:

#
ingress:
  - hostname: YOUR-DOMAIN
    service: http://127.0.0.1:18789
  - service: http_status:404

Save, then restart cloudflared:

sudo systemctl restart cloudflared

Then test:

#
curl -i https://YOUR-DOMAIN/healthz

If you don’t know where the config is, run:

cloudflared tunnel ingress validate
onyx solstice
#

but why do i need to edit it it asn changed

sudden valeBOT
#

You might not need to edit it. First verify what Cloudflared is actually doing.

A config can be unchanged but still wrong because:

  • cloudflared restarted and loaded a different config file
  • the service started before/without the same env/working dir
  • the hostname rule doesn’t match your current domain
  • a catch-all rule is catching /healthz
  • your custom UI/proxy is intercepting the path

Run these:

cloudflared tunnel ingress validate
cloudflared tunnel ingress rule https://YOUR-DOMAIN/healthz
curl -i http://127.0.0.1:18789/healthz
curl -i https://YOUR-DOMAIN/healthz
#

The important one is:

cloudflared tunnel ingress rule https://YOUR-DOMAIN/healthz

It tells you exactly which ingress rule Cloudflared will use. If it doesn’t point to http://127.0.0.1:18789, then that’s why the domain /healthz fails.

onyx solstice
#

with the 1st one no config file was found

sudden valeBOT
#

Aha — that explains the ā€œnothing changedā€ confusion.

cloudflared tunnel ingress validate saying no config file found means the command isn’t seeing a local config.yml in the default place. Your running cloudflared may be using:

  • a different config path
  • a systemd service with --config /some/path
  • a dashboard/quick tunnel setup with no local ingress config
  • a different user’s home directory

Find what the running service uses:

ps aux | grep '[c]loudflared'
systemctl status cloudflared --no-pager
systemctl cat cloudflared
#

Look for --config ... or tunnel run ....

If you find a config path, validate it explicitly:

cloudflared tunnel --config /PATH/TO/config.yml ingress validate
cloudflared tunnel --config /PATH/TO/config.yml ingress rule https://YOUR-DOMAIN/healthz

If there’s no config path at all, you’re probably not running a local config-based tunnel, so editing ~/.cloudflared/config.yml won’t affect the running tunnel.

hardy flicker
#

what operating system are you on ?

sudden valeBOT
#

If you mean the Raspberry Pi, run:

cat /etc/os-release
uname -a

That’ll tell us the OS/distro and kernel.

livid merlin
#

This threads is getting very long - please start a new one

trim cliffBOT
sudden valeBOT
#

Agreed — this one’s gotten too tangled.

Please start a new #help thread and include:

Goal: expose OpenClaw through Cloudflared on Raspberry Pi

Confirmed:
- local http://127.0.0.1:18789/healthz returns {"ok":true,"status":"live"}
- Cloudflared/config path is unclear / validate said no config file found

Still broken:
- domain /healthz does not return the same JSON

Paste:
- openclaw --version
#
- cat /etc/os-release
- ps aux | grep '[c]loudflared'
- systemctl cat cloudflared
- curl -i http://127.0.0.1:18789/healthz
- curl -i https://YOUR-DOMAIN/healthz

That’ll make it much easier to help without all the old noise.