#Why is my bot taking extra long to reply on telegram
1 messages · Page 1 of 1 (latest)
A few different things can look like “slow replies” on Telegram, so the fastest path is: confirm which bucket you’re in, then grab 2 outputs.
First: what kind of “slow”?
-
Only when you send multiple messages quickly (or you keep talking while it’s thinking)
→ this is often just queue mode (default is “collect”, which intentionally waits/batches).
In the Telegram chat, send:/queueand paste what it says.
Docs: https://docs.openclaw.ai/help/faq#why-does-it-feel-like-the-bot-ignores-rapidfire-messages -
Even for a single short message, sometimes it takes 30–120s
→ usually either model/provider latency, gateway busy, or Telegram API/network hiccups. -
It’s fast in DMs but slow in a group/topic
→ Telegram processing is sequenced per chat/thread, so if one long run is in-flight in that same chat, later messages wait their turn.
Two things to paste here (they’re safe / redact secrets)
On the machine running the Gateway:
openclaw --version
openclaw status --all
If you can, also run this right when it’s being slow and paste ~30 lines around the delay:
openclaw logs --follow
Common concrete causes (what I’ll look for in the logs)
- Queue mode = collect (it will “wait a bit” to batch rapid-fire messages)
- Long-running tool work blocking the chat (try
stopin Telegram when it’s stuck) - Telegram polling/network instability (logs often show
fetch failed/getUpdates failed; sometimes IPv6 egress issues) - Gateway overloaded (CPU/RAM) or too low
agents.defaults.maxConcurrentrelative to how many chats/tasks you run
Drop the --version + status --all output and whether this is Telegram DM vs group, and we can narrow it down quickly.
18:58:36 warn compaction-safeguard {"subsystem":"compaction-safeguard"} Compaction safeguard: cancelling compaction with no real conversation messages to summarize.
18:58:36 warn agent/embedded {"subsystem":"agent/embedded"} embedded run agent end: runId=edcfedc1-6d86-4985-968b-60e6fe9174a9 isError=true error=400 tool calling is not supported with this model
18:58:37 warn agent/embedded {"subsystem":"agent/embedded"} embedded run agent end: runId=c17e5313-4fef-4ffd-8325-9d34c91f32cb isError=true error=400 tool calling is not supported with this model
18:58:37 info gateway/channels/telegram {"subsystem":"gateway/channels/telegram"} telegram sendMessage ok chat=6468789862 message=3251
18:58:41 warn agent/embedded {"subsystem":"agent/embedded"} embedded run agent end: runId=1661bc0a-3d5e-48c1-bd74-28b370f73935 isError=true error=400 tool calling is not supported with this model
Those log lines basically explain the “slow sometimes”:
error=400 \tool calling` is not supported with this model`
OpenClaw is trying to run the agent with tools enabled (normal), but the currently selected model (or one of your fallbacks) doesn’t support tool calling, so the run errors, then it retries/fails over — that extra churn is what you feel as “it took forever / it was flaky”.
Fix (what to do next)
- On the gateway host, paste:
openclaw models status
This will show which model/provider you’re actually running (and usually makes it obvious if you accidentally picked a non-tool model).
- In the Telegram chat itself, try:
/model status/model list→ pick a model that supports tools/tool-calling (anything intended for agent/tool use)
- If you’re using a local model (common culprit):
- Ollama: if your
baseUrlends with/v1(OpenAI-compatible mode), that frequently breaks tool calling. Use the native base URL instead (no/v1). Docs: https://docs.openclaw.ai/providers/ollama
After you switch
Trigger one message that used to be slow and watch logs — the tool calling is not supported warnings should disappear entirely.
If you paste openclaw models status output, I can tell you exactly which configured model entry is causing the retries.
⚠️ 📝 Edit: in ~/.openclaw/workspace/TOOLS.md (151 chars) failed
That warning is not a Telegram/network issue — it’s OpenClaw telling you:
- the agent tried to apply an “edit/patch” to
~/.openclaw/workspace/TOOLS.md - and the edit couldn’t be applied
The two most common reasons:
- Patch mismatch: the “edit” operation expects the file to contain an exact snippet, but your
TOOLS.mddoesn’t match (changed since last read, different whitespace, etc.). - Workspace not writable: gateway is running under a different user / inside a sandbox / permissions issue, so it can’t write to
~/.openclaw/workspace.
What I need to tell which one it is
On the gateway host, paste the log lines around it (there’s usually a real error right before/after):
openclaw logs --follow
(or just re-trigger once and immediately tail logs)
Quick mitigations
- If you don’t want to see these internal tool warnings in Telegram: send
/verbose off(they show up when verbose/tool output is enabled). Docs: https://docs.openclaw.ai/help/faq#how-do-i-stop-internal-system-messages-from-showing-in-chat - If it’s a patch mismatch: ask the bot to read the file first, then rewrite the whole file (or you edit it manually). “Edit/patch” is brittle; “write/overwrite” is reliable.
If you paste the log chunk, I can point to the exact cause (mismatch vs permissions/sandbox).
17:30:43 info gateway/channels/telegram {"subsystem":"gateway/channels/telegram"} telegram sendMessage ok chat=6468789862 message=4014
17:30:44 info gateway/channels/telegram {"subsystem":"gateway/channels/telegram"} telegram sendMessage ok chat=6468789862 message=4015
17:30:45 info gateway/channels/telegram {"subsystem":"gateway/channels/telegram"} telegram sendMessage ok chat=6468789862 message=4016
17:31:01 error [tools] edit failed: Could not find the exact text in /home/admin/.openclaw/workspace/TOOLS.md. The old text must match exactly including all whitespace and newlines.
17:31:01 error [tools] edit failed: Could not find the exact text in /home/admin/.openclaw/workspace/TOOLS.md. The old text must match exactly including all whitespace and newlines.
17:31:11 info gateway/channels/telegram {"subsystem":"gateway/channels/telegram"} telegram sendMessag
Yep — that’s the best possible error to get, because it’s very specific:
edit failed: Could not find the exact text … The old text must match exactly …
So nothing is “broken” with permissions. The edit tool is an exact find-and-replace. If the agent tries to patch based on a guessed snippet (or the file changed since it last read it), the edit is rejected on purpose.
How to fix it (pick one)
-
Reliable approach: tell the bot to read the file first, then make the change.
- Prompt idea: “Read
/home/admin/.openclaw/workspace/TOOLS.mdand then update it. Don’t guess file contents.”
- Prompt idea: “Read
-
Even more reliable: tell it to rewrite the whole file (overwrite) instead of doing a surgical edit.
- This avoids the “oldText must match exactly” constraint.
-
Manual: just edit
TOOLS.mdyourself in an editor; this file is just workspace notes.
Why it can feel like “slow replies”
If the bot keeps attempting these edits (and failing), it’s doing extra model turns / retries, which adds latency and wastes tokens. So getting it to “read first” or “rewrite” will also reduce that overhead.
If you want, paste the current TOOLS.md (or the section you want to change) and the exact change you wanted — then I can tell you the safest edit strategy (append vs rewrite) so it won’t keep failing.
Timed out after 60s waiting for gateway port 18789 to become healthy.
Service runtime: status=running, state=active, pid=96240, lastExit=0
Port 18789 is already in use.
- Port is in use but process details are unavailable (install lsof or run as an admin user).
Port diagnostics errors: Error: spawn lsof ENOENT
Gateway restart timed out after 60s waiting for health checks.
That error means the restart couldn’t bring the Gateway back healthy because 18789 never became free/healthy:
- service says it’s “running” (pid
96240) - but port 18789 is already in use
- and OpenClaw couldn’t auto-diagnose the port owner because
lsofisn’t installed (spawn lsof ENOENT)
1) First, paste these two outputs
openclaw --version
openclaw gateway status
gateway status will tell us whether the service thinks it’s listening, what URL it’s probing, and often the last startup error.
2) Find what’s actually bound to 18789 (no lsof needed)
Try one of these (Linux):
ss -lptn | grep ':18789'
If that doesn’t show the process name due to permissions, run with sudo:
sudo ss -lptn | grep ':18789'
(Alternative: sudo fuser -v 18789/tcp)
3) Most common cause
You have two gateway instances:
- the service is running, and/or
- you also started
openclaw gatewaymanually in a terminal earlier
Fix pattern:
- stop the service:
openclaw gateway stop
- confirm the port is free (
ss … :18789) - start it again:
openclaw gateway start
4) If you want the nicer diagnostics
Install lsof so OpenClaw can print the port owner automatically:
sudo apt-get update && sudo apt-get install -y lsof
If you paste openclaw gateway status + the ss output, I can tell you exactly whether it’s “duplicate gateway” vs “gateway hung but still holding the port”, and the cleanest next step.
Hey! What's up?
Pre-compaction memory flush. Store durable memories now (use memory/2026-03-05.md; create memory/ if needed). IMPORTANT: If the file already exists, APPEND new
content only and do not overwrite existing entries. If nothing to store, reply with NO_REPLY.
Current time: Thursday, March 5th, 2026 — 7:14 AM (UTC)
That Telegram message is OpenClaw’s internal “pre-compaction memory flush” prompt leaking into the chat. It’s supposed to be a silent housekeeping turn (so you should never see it as a user-facing message).
When it leaks, it’s usually one of these:
- You’re on an older build (or a build where silent-turn suppression is buggy)
- You customized
agents.defaults.compaction.memoryFlush.prompt/systemPromptand accidentally removed theNO_REPLYbehavior - You have verbose/reasoning output enabled, so internal turns get shown
Quick fixes
In the Telegram chat, send:
/verbose off/reasoning off
Docs: https://docs.openclaw.ai/help/faq#how-do-i-stop-internal-system-messages-from-showing-in-chat
Confirm your config + version (paste outputs)
On the gateway host:
openclaw --version
openclaw config get agents.defaults.compaction.memoryFlush
If you just want to disable the feature (stops the flush turns entirely)
Set:
{
agents: {
defaults: {
compaction: { memoryFlush: { enabled: false } },
},
},
}
Reference (this is exactly what that feature is): https://docs.openclaw.ai/reference/session-management-compaction#pre-compaction-memory-flush-implemented
If you paste the version + memoryFlush config, I can tell you whether this is “upgrade fixes it” vs “prompt override is causing the leak”.
this is dispalyin on the tui instead, is it an issue or no?
If it’s only showing in the TUI, that’s generally not a problem.
The TUI chat log includes system notices + tool cards, and the “pre-compaction memory flush” is an internal housekeeping turn that can show up there (especially if you have /verbose on or are showing more system output). TUI doc even calls out that it shows system notices in the chat log: https://docs.openclaw.ai/web/tui
When it is an issue:
- if the same “memory flush” text is being delivered to Telegram / real users as an assistant message (spammy/confusing), or
- if it’s happening constantly (means you’re hitting compaction thresholds a lot → extra turns → more latency)
If you want it quieter in TUI/overall:
/verbose offand/reasoning off(in the TUI session)- or disable the feature via
agents.defaults.compaction.memoryFlush.enabled: false(but I’d only do that after confirming your version/config)
If you tell me: does it appear as a “system notice” line or as an actual assistant reply in the TUI? I can say whether it’s expected display vs a leak/misconfig.
got it like twice now though but seems like this is a different session
Yep — that tracks.
The pre-compaction memory flush is tracked per session key, so if you have multiple sessions (for example: TUI main session vs a Telegram DM session vs a group/topic session), you can see it once in each.
A couple quick ways to sanity-check:
- In the TUI, when it happens, run
/statusand look at the session key shown in the footer/output. - If you see different keys (or you recently did
/new/ a restart caused a new session), then seeing the flush twice is expected.
It’s only “weird” if you see it repeatedly in the exact same session with little conversation — then it usually means you’re hitting compaction thresholds very quickly (small context model, lots of tool output, verbose on, etc.).
If you paste /status from the TUI right after it appears (and tell me whether Telegram is sharing that same session key or not), I can tell you if it’s normal or if your sessions are getting split unexpectedly.
07:28:22 warn plugins {"subsystem":"plugins"} [plugins] openclaw-web-search: loaded without install/load-path provenance; treat as untracked local code and pin trust via plugins.allow or install records (/home/admin/.openclaw/extensions/openclaw-web-search/index.ts)
07:28:22 info ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
██░▄▄▄░██░▄▄░██░▄▄▄██░▀██░██░▄▄▀██░████░▄▄▀██░███░██
██░███░██░▀▀░██░▄▄▄██░█░█░██░█████░████░▀▀░██░█░█░██
██░▀▀▀░██░█████░▀▀▀██░██▄░██░▀▀▄██░▀▀░█░██░██▄▀▄▀▄██
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
🦞 OPENCLAW 🦞
That line is just a startup security warning, not an error:
openclaw-web-search: loaded without install/load-path provenance; treat as untracked local code …
It means the Gateway found a plugin on disk at:
/home/admin/.openclaw/extensions/openclaw-web-search/index.ts
…but it wasn’t installed/recorded via the plugin installer (so OpenClaw can’t tell if it’s “intentionally installed” vs “random code that appeared in extensions/”). It still loads, but warns so you can pin trust explicitly.
If you trust it (recommended): pin it in config
- Check the exact plugin id:
openclaw plugins list
- Add it to
plugins.allow(and ensure it’s enabled), e.g.:
{
plugins: {
allow: ["openclaw-web-search"],
entries: {
"openclaw-web-search": { enabled: true },
},
},
}
Then restart the gateway (plugin config requires restart).
Docs (this exact warning is described here):
https://docs.openclaw.ai/tools/plugin#config
If you don’t want it
Either disable it under plugins.entries.<id>.enabled: false, or remove it from ~/.openclaw/extensions/.
This warning itself won’t make Telegram replies slow — but the plugin can add latency if the agent decides to use web search during a reply.
I don't ususalyy get this but i do now while using tui and I don't know why when I hit rate limit once on tui, it doens't respond again more like as if my messages are been dropped or somthing
That “rate limited once → then it feels like messages are dropped” pattern is almost always one of these in the TUI:
- You’re still in an in‑flight run (stuck/slow/backing off), so new messages get queued/collected instead of answered.
- Your queue mode is set to something that batches/drops while a run is active (often “collect”).
- Provider cooldown/backoff kicked in after a 429, and you don’t have a usable fallback model/profile, so every new send immediately fails (sometimes it only shows in logs, depending on verbose).
Quick checks in the TUI (no logs needed yet)
- Run:
/status
Look at whether it says the session is running / error / idle.
-
If it’s “running” and not progressing, hit Esc (abort), then try a fresh short message.
-
Check queue mode:
/queue
If it’scollect, switch to something more predictable while debugging, e.g. followup (so each message gets a clear turn) or interrupt (so new messages cancel the old run).
(Your/queueoutput will tell us what it’s currently doing.)
Then grab one log snippet (this will show the real reason)
On the gateway host:
openclaw logs --follow
Reproduce: send one message in TUI → wait for the “no response” behavior → copy the relevant lines (especially any 429 / rate_limit / cooldown lines).
If you paste:
/statusoutput (from TUI)/queueoutput (from TUI)- the log chunk around the rate limit
…I can tell you whether it’s stuck-run queuing vs provider cooldown vs “all models failed / no fallback” and what the clean fix is (usually: set a fallback model/profile, or change queue mode).
Gateway status
Link channel: unknown
Heartbeat: 5m (main), disabled (openrouter-skills-create-agent)
Session stores: 2
Default model: pixtral-large-2411 (32k ctx)
Active sessions: 16
Recent sessions:
-
agent:main:main [direct] | just now | model mistral-large-latest | tokens ?/131k | flags: system, id:3217734a-e15a-413f-87d8-9a101e18cf75
-
agent:main:telegram:direct:6468789862 [direct] | 36m ago | model pixtral-large-2411 | tokens 16k/32k (16k left, 51%) | flags: system,
id:06e860e4-f188-4a6c-8ec6-ba82bc3ba394 -
agent:main:telegram:group:-1003733326461 [group] | 46m ago | model gemini-2.5-flash | tokens ?/32k | flags: system, id:e25ce1d5-45cd-44d0-873d-53aa6991de2a
That status output is super helpful — and it explains a bunch of what you’re seeing.
1) Yes, those are different sessions
You currently have (at least):
agent:main:main(that’s your TUI session)agent:main:telegram:direct:6468789862(your Telegram DM session)- plus a Telegram group session
So anything “per-session” (memory flush, compaction, rate limits, queue behavior) can happen independently in each.
Docs on how session keys work: https://docs.openclaw.ai/concepts/session
2) The big red flag: Heartbeat every 5 minutes
Your status shows:
Heartbeat: 5m (main)
That’s extremely aggressive. If your provider rate-limits you once, a 5‑minute heartbeat can keep re-triggering calls in the background and make it feel like the TUI is ignoring you (busy / cooling down / failing fast).
If you don’t need that frequency, bump it back up or disable while debugging. FAQ shows the config shape here: https://docs.openclaw.ai/help/faq#why-am-i-getting-heartbeat-messages-every-30-minutes
3) Why it feels like messages are “dropped” after a 429
Usually it’s not dropped — it’s one of:
- the run is still “busy” (new messages get queued/collected), or
- the provider/model is in cooldown/backoff after the rate limit, so every new request fails or waits
To confirm which: when it happens in TUI, run /status and check if it says running vs idle/error, and tail logs:
openclaw logs --follow
If you paste the log lines around the actual 429/rate limit event, I can tell you whether it’s (a) heartbeat hammering, (b) no fallback model configured, or (c) a queue-mode “collect” situation.
is the heartbeat time too soon?
Yeah — 5 minutes is usually way too soon unless you specifically want constant background activity.
Heartbeat is a real agent run. So at every 5m you’re:
- spending tokens even when you’re not chatting
- increasing the chance of provider rate limits / cooldowns
- potentially keeping the gateway “busy”, which can make the TUI/Telegram feel laggy
Most people do something like 30m–2h, or just disable it and use cron for scheduled work.
To change it (from the FAQ):
{
agents: {
defaults: {
heartbeat: {
every: "2h" // or "30m"
// use "0m" to disable
}
}
}
}
Docs: https://docs.openclaw.ai/help/faq#why-am-i-getting-heartbeat-messages-every-30-minutes
If you tell me what you’re using heartbeat for (reminders? monitoring? “stay alive”?), I can suggest a sane interval + whether cron is a better fit.
so cron is better than heartbeat?
Cron isn’t strictly “better” — it’s better for different jobs.
Rule of thumb (and it matters for your rate-limit/lag issue):
Use Heartbeat when you want “periodic awareness”
- “Every ~30 minutes, check a few things and only ping me if something matters”
- It runs in the main session, so it’s context-aware and can batch multiple checks into one turn.
- But if you set it too aggressive (like your 5m), it can burn tokens and trigger rate limits / keep the agent busy.
Use Cron when you need precision + isolation
- “At 9:00am, send a daily summary”
- “Remind me in 20 minutes”
- “Run a heavy report weekly”
- Cron can run in an isolated session (
cron:<jobId>), so it won’t pollute or block your main/TUI session as much, and you can tune model/thinking per job.
Best practice is usually both:
- Heartbeat at a sane interval (often 30m–2h)
- Cron for exact schedules + one-shot reminders
Docs: https://docs.openclaw.ai/automation/cron-vs-heartbeat
Given you’re seeing rate-limit weirdness: I’d first raise heartbeat from 5m (or disable it), then move any “must run at X time” tasks to cron.
does cron uses model api calls?
Yes — cron jobs normally trigger a real agent run, which means LLM/model calls (API calls if you’re using a hosted provider).
Cron is mainly about scheduling + isolation/precision, not “free automation”.
Nuance:
- If you schedule a cron job with
--message ...(or--announce), that’s an agent turn → model call. - A
--system-event "..."can be just an event written into the session store, but if you add--wake now(common for reminders), you’re waking the agent to react → model call.
Docs: https://docs.openclaw.ai/automation/cron-jobs
If your goal is lower cost / fewer rate limits, the levers are:
- run it less frequently (heartbeat/cron interval)
- use a cheaper model for those scheduled runs (cron supports per-job
--model, heartbeat has its own config too)
how do I remove configured models and custom providers
You remove them by unsetting the config paths where you added them (and sometimes by deleting the per‑agent models.json override).
1) Find where they’re configured
On the gateway host:
openclaw config file
openclaw config get models.providers
openclaw config get agents.defaults.model
openclaw config get agents.defaults.models
2) Remove a custom provider
Custom providers live under models.providers. Unset the one you don’t want (bracket notation is safest, especially if the id has -):
openclaw config unset 'models.providers["<providerId>"]'
If you want to wipe all custom providers:
openclaw config unset models.providers
3) Remove “configured models” / aliases / allowlists
Those are typically in agents.defaults.models (aliases/metadata) and agents.defaults.model (primary + fallbacks). To clear them:
openclaw config unset agents.defaults.models
# and/or selectively remove fallbacks etc:
openclaw config unset agents.defaults.model.fallbacks
Important: make sure your default model doesn’t point at a provider/model you’re deleting (use openclaw models status / openclaw models set … first), otherwise the gateway may start up with “unknown model/provider” errors.
4) Check per-agent overrides too (common gotcha)
Custom providers can also be defined in:
~/.openclaw/agents/<agentId>/agent/models.json
If that file exists, either remove the provider entry from it or move the file aside.
5) Restart
Plugin/provider config changes generally require a restart:
openclaw gateway restart
If you paste your openclaw config get models.providers output (redact keys), I can tell you the exact config unset … commands for your provider ids.
admin@ip-172-31-28-204:~$ openclaw config unset 'models.providers["custom-api-groq-com"]'
🦞 OpenClaw 2026.3.2 (85377a2) — I run on caffeine, JSON5, and the audacity of "it worked on my machine."
Config path not found: models.providers["custom-api-groq-com"]
admin@ip-172-31-28-204:~$ openclaw config unset 'models.providers["custom-api-groq-com"]'
🦞 OpenClaw 2026.3.2 (85377a2) — I'm the assistant your terminal demanded, not the one your sleep schedule requested.
Config path not found: models.providers["custom-api-groq-com"]
Config path not found just means that provider isn’t in your main openclaw.json config under models.providers (so there’s nothing there for config unset to remove).
In OpenClaw, custom providers can live in either:
- main config:
~/.openclaw/openclaw.json→models.providers - per-agent override:
~/.openclaw/agents/<agentId>/agent/models.json
Docs reference: https://docs.openclaw.ai/gateway/configuration-reference#custom-providers-and-base-urls
Step 1: list what’s actually in config
Run:
openclaw config get models.providers
If it prints null / {} / nothing relevant, then it’s not in config.
Step 2: check per-agent models.json
This is the usual culprit:
ls -la ~/.openclaw/agents/*/agent/models.json
If you find one, open it and look for that provider id. Quick, reversible “remove everything custom” test is to move it aside:
mv ~/.openclaw/agents/<agentId>/agent/models.json ~/.openclaw/agents/<agentId>/agent/models.json.bak
openclaw gateway restart
(Replace <agentId> with whatever directory exists, often main.)
If you want, paste the output of:
openclaw config get models.providersls -la ~/.openclaw/agents/*/agent/models.json
…and I’ll tell you the exact path/id to unset (or which models.json file to edit).
{
"custom-infinitekeys-fun": {
"baseUrl": "https://infinitekeys.fun/proxy/v1",
"apiKey": "OPENCLAW_REDACTED",
"api": "openai-completions",
"models": [
{
"id": "gemini-2.5-flash",
"name": "gemini-2.5-flash (Custom Provider)",
"reasoning": false,
"input": [
"text"
],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 32000,
"maxTokens": 4096,
"api": "openai-completions"
}
]
},
"custom-openrouter-ai": {
"baseUrl": "https://openrouter.ai/api/v1",
"apiKey": "OPENCLAW_REDACTED",
"api": "openai-completions",
"models": [
{
"id": "arcee-ai/trinity-large-preview:free",
"name": "arcee-ai/trinity-large-preview:free (Custom Provider)",
"reasoning": false,
"input": [
"text"
],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 32000,
"maxTokens": 4096,
"api": "openai-completions"
}
]
},
"custom-api-groq-com": {
"baseUrl": "https://api.groq.com/openai/v1",
"apiKey": "OPENCLAW_REDACTED",
"api": "openai-completions",
"models": [
{
"id": "groq/compound",
"name": "groq/compound (Custom Provider)",
"api": "openai-completions",
"reasoning": false,
"input": [
"text"
],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 32000,
"maxTokens": 4096
},
{
Ahh — this is a path syntax gotcha in openclaw config.
Inside [...] the CLI does not strip quotes. So when you wrote:
models.providers["custom-api-groq-com"]
it literally looked for a key named "custom-api-groq-com" (including the "), which doesn’t exist → “path not found”.
Use no quotes:
openclaw config unset 'models.providers[custom-api-groq-com]'
Or (this also works fine for - keys):
openclaw config unset models.providers.custom-api-groq-com
Then restart:
openclaw gateway restart
Same pattern for your other custom providers, e.g. models.providers.custom-openrouter-ai, etc.
it's still showing when I check /model though
If it’s still showing, one of these is happening:
- the
unsetdidn’t actually hit the config you’re viewing (wrong config file / wrong profile), or - you removed it from
openclaw.jsonbut it’s still coming from the per‑agentmodels.jsonoverride.
Let’s pinpoint which in 30 seconds.
1) Verify the unset actually worked (in the same shell)
Run:
openclaw config get models.providers.custom-api-groq-com
openclaw config file
- If
config get ...custom-api-groq-comstill prints an object → it wasn’t removed. config filetells us which file the CLI is editing/reading.
2) Make sure the service is using the same config
Run:
openclaw gateway status
That output includes “Config (cli)” vs “Config (service)” when they differ. If they’re different, you’re editing one file but checking/running the other.
3) Check per-agent override file (common)
ls -la ~/.openclaw/agents/*/agent/models.json
If that exists, it can keep re-introducing custom providers even after you remove them from openclaw.json.
Paste the output of openclaw config file + openclaw gateway status and I’ll tell you exactly where it’s still coming from and the exact command to remove it.
-rw-rw-r-- 1 admin admin 58003 Mar 5 08:04 /home/admin/.openclaw/agents/main/agent/models.json
That explains why it “keeps coming back”: ~/.openclaw/agents/main/agent/models.json is a per‑agent model catalog override, and it can define custom providers independently of openclaw.json.
You have two options (both safe):
Option A (quick “nuke custom providers for this agent”)
Move it aside and restart:
mv /home/admin/.openclaw/agents/main/agent/models.json \
/home/admin/.openclaw/agents/main/agent/models.json.bak
openclaw gateway restart
This is reversible (just move it back).
Option B (remove only one provider from that file)
First list provider ids without exposing keys:
jq '.providers | keys' /home/admin/.openclaw/agents/main/agent/models.json
Then delete just the one you want and write the file back (also no key output):
cp /home/admin/.openclaw/agents/main/agent/models.json \
/home/admin/.openclaw/agents/main/agent/models.json.bak
jq 'del(.providers["custom-api-groq-com"])' \
/home/admin/.openclaw/agents/main/agent/models.json.bak \
> /home/admin/.openclaw/agents/main/agent/models.json
openclaw gateway restart
(If jq isn’t installed, tell me and I’ll give you a nano/manual edit snippet.)
One more note: that file contains API keys, so you probably want to lock permissions down afterward:
chmod 600 /home/admin/.openclaw/agents/main/agent/models.json
I deleted the file but I can still see it btw
A couple possibilities here — “I can still see it” depends where you’re looking.
1) If you mean openclaw config get models.providers
Deleting /home/admin/.openclaw/agents/main/agent/models.json won’t change that output at all, because that command reads your main config (~/.openclaw/openclaw.json).
So you also need to remove the provider(s) from config, e.g.:
openclaw config unset models.providers.custom-api-groq-com
# (repeat for the other custom-* providers)
Or if you want to wipe all custom providers in one go:
openclaw config unset models.providers
2) If you mean openclaw models status / openclaw models list
models statuscan still reflect what the running gateway loaded until you restart.models listwill still show built-in catalog entries, but yourcustom-*provider ids should disappear once they’re truly removed.
So after changes, do:
openclaw gateway restart
Quick check
Can you paste the exact command you run to “see it”, and what string you’re still seeing (e.g. custom-api-groq-com vs just groq)? That tells us immediately whether it’s still in config, still cached in the running gateway, or just the built-in catalog.
I see it in the /model list
…finish that thought for me: “I see it in the ____” (TUI model picker? /model list? Control UI? openclaw models list?).
In the meantime, here are the two most common “still seeing it” cases:
A) You see it in the TUI model picker / /model list
That list comes from the running gateway’s current model catalog. If you removed files/entries but didn’t restart the gateway, the TUI will still show the old catalog until restart + reconnect.
Do:
openclaw gateway restart
# then restart/reconnect the TUI
openclaw tui
B) You still see it as the current session model
Even if you remove the provider, an existing session can stay “pinned” to that model until you switch/reset. In TUI:
/model→ pick a different model- then
/new(fresh session) if needed
The definitive check (no UI ambiguity)
This tells us whether the provider is actually still configured in openclaw.json:
openclaw config get models.providers
If you paste:
- where you “see it” (exact command/screen), and
- the output of
openclaw config get models.providersafter your changes,
…I can tell you exactly whether it’s a restart/cache/session-pin thing or the provider is still in config.
no longer there, how do I disable heartbeat btw
To disable heartbeat globally (for the default agent):
openclaw config set agents.defaults.heartbeat.every "0m"
openclaw gateway restart
Then verify:
openclaw status
Docs: https://docs.openclaw.ai/help/faq#why-am-i-getting-heartbeat-messages-every-30-minutes
If you only want to disable it for one specific agent (multi-agent setup), tell me the agent id (from openclaw agents list) and I’ll give you the exact agents.list[...].heartbeat path.
CRITICAL Open groupPolicy with elevated tools enabled
Found groupPolicy="open" at: - channels.telegram.groupPolicy With tools.elevated enabled, a prompt injection in those rooms can become a high-impact incident.
Fix: Set groupPolicy="allowlist" and keep elevated allowlists extremely tight.
CRITICAL Open groupPolicy with runtime/filesystem tools exposed
Found groupPolicy="open" at: - channels.telegram.groupPolicy Risky tool exposure contexts: - agents.defaults (sandbox=off; runtime=[exec, process]; fs=[read, w…
Fix: For open groups, prefer tools.profile="messaging" (or deny group:runtime/group:fs), set tools.fs.workspaceOnly=true, and use agents.defaults.sandbox.mode="all" for exposed agents.
CRITICAL Extensions exist but plugins.allow is not set
Found 1 extension(s) under /home/admin/.openclaw/extensions. Without plugins.allow, any discovered plugin id may load (depending on config and plugin behavior)…
Fix: Set plugins.allow to an explicit list of plugin ids you trust.
CRITICAL Telegram security warning
Telegram groups: groupPolicy="open" allows any member in allowed groups to trigger (mention-gated). Set channels.telegram.groupPolicy="allowlist" + channels.te…
Those “CRITICAL” lines are real warnings (usually from openclaw security / openclaw doctor) and they’re basically saying:
- your Telegram groups are open (
channels.telegram.groupPolicy="open") - while you have high-impact tools available (runtime/filesystem and even elevated mode)
- plus you have at least one local plugin but no
plugins.allowpinning
That combo is exactly how prompt-injection incidents happen in group chats.
Quick “make it safe” fixes
1) Lock Telegram groups to an allowlist
If you only want you to be able to trigger the bot in groups:
openclaw config set channels.telegram.groupPolicy "allowlist"
openclaw config set channels.telegram.groupAllowFrom '["tg:6468789862"]' --strict-json
(Use your own Telegram numeric user id there — 6468789862 looks like it might be yours, but double-check from logs from.id in a DM.)
Docs: https://docs.openclaw.ai/channels/telegram#group-policy-and-allowlists
2) Pin plugin trust (stop the “extensions exist but plugins.allow not set” warning)
First list plugin ids:
openclaw plugins list
Then set an allowlist (example, replace ids with what you actually have):
openclaw config set plugins.allow '["openclaw-web-search"]' --strict-json
Docs: https://docs.openclaw.ai/tools/plugin#config
3) Restart + re-check
openclaw gateway restart
openclaw security
If you paste your openclaw plugins list output (just ids) and confirm whether 6468789862 is your Telegram user id, I can give you the exact final config lines with no guesswork.
Yep — channels.telegram.groups entries like:
channels: {
telegram: {
groups: {
"-100123...": {}, // <- empty object
},
},
}
are basically “this group is allowlisted, use defaults”.
An empty {} means the group inherits the global defaults, especially:
channels.telegram.groupPolicy
channels.telegram.groupAllowFromrequireMentiondefaults (unless you override)
So if your global channels.telegram.groupPolicy is "open", then every group you list as {} is effectively open too.
How to do per-group policies properly
Set a safe global default, then override only where needed:
{
channels: {
telegram: {
groupPolicy: "allowlist",
groupAllowFrom: ["tg:6468789862"],
groups: {
"-100AAA": {}, // inherits allowlist + your sender allowlist
"-100BBB": { groupPolicy: "open", requireMention: true }, // only if you really want open
"-100CCC": { allowFrom: ["tg:6468789862", "tg:123"] }, // per-group sender list override
},
},
},
}
Docs (this exact inheritance/override model): https://docs.openclaw.ai/channels/telegram#group-policy-and-allowlists
Given your security warnings: if you truly need a group to be open, I’d strongly recommend doing that only on a “messaging-only tools” agent (or deny runtime/fs tools for that agent), otherwise it’s a prompt-injection footgun.
If you have:
channels: {
telegram: {
groups: {
"*": { /* …policy… */ }
}
}
}
"*" is not “just defaults” — it effectively means “apply this config to all Telegram groups” and (importantly) it also behaves like an allowlist wildcard: any group the bot is in can pass the “is this group allowed?” check.
That’s why the security tool freaks out when groupPolicy="open" + tools are exposed.
Safer patterns
Pattern 1 (recommended): only specific groups
channels: {
telegram: {
groupPolicy: "allowlist",
groups: {
"-1001234567890": { requireMention: true },
"-1009876543210": { requireMention: true },
},
},
}
Pattern 2 (ok if you really want wildcard groups): any group, but only YOU can trigger
channels: {
telegram: {
groupPolicy: "allowlist",
groupAllowFrom: ["tg:6468789862"],
groups: {
"*": { requireMention: true },
},
},
}
So: keep groups["*"] only if you truly want global group behavior — and then absolutely do groupPolicy: "allowlist" + groupAllowFrom (and keep requireMention: true).
for each group allow list, which I used *, I have few list in it for just my telegram id and my alt accounts
That’s a perfectly reasonable setup — the missing piece is just which knob controls “who can trigger in groups.”
If you want “any Telegram group I’m in is allowed (*), but only these Telegram user IDs can trigger the bot”, do this:
channels: {
telegram: {
groupPolicy: "allowlist",
// who is allowed to trigger in groups:
groupAllowFrom: ["tg:YOUR_ID", "tg:ALT_ID_1", "tg:ALT_ID_2"],
// which groups are allowed + defaults:
groups: {
"*": { requireMention: true },
},
},
}
Key points:
groups: { "*": … }controls which groups are allowed (wildcard = any group the bot is in) + per-group defaults.groupPolicy+groupAllowFromcontrols which senders inside those groups are allowed to trigger.- Once you set
groupPolicy: "allowlist"andgroupAllowFrom: [...], those CRITICAL “open groupPolicy” warnings should go away (because it’s no longer open).
If you paste your current channels.telegram block (redact tokens), I can point out exactly where to put the IDs so it matches what you intended.
"channels": {
"telegram": {
"enabled": true,
"dmPolicy": "pairing",
"botToken": "87850012...........nwbn8kWXFA",
"groups": {
"*": {
"requireMention": true,
"groupPolicy": "allowlist",
"allowFrom": [
"tg:6468789862",
"tg:6514163528",
"tg:1976509101",
"tg:7091956351"
]
}
},
"groupPolicy": "open",
"streaming": "partial"
}
},
You’ve basically already configured what you want — the only problem is this line:
"groupPolicy": "open",
Because it’s set at the global level (channels.telegram.groupPolicy), the security checker flags it (and in some edge cases global defaults can still “win” when a per-group override doesn’t apply the way you expect).
Fix: make the global policy match your per-group policy
Set the global group policy to allowlist:
openclaw config set channels.telegram.groupPolicy "allowlist"
openclaw gateway restart
openclaw security
Optional cleanup (simplifies config)
Right now you’re using per-group allowFrom under "*"; that works, but the more “canonical” place is global groupAllowFrom:
channels: {
telegram: {
groupPolicy: "allowlist",
groupAllowFrom: ["tg:6468789862", "tg:6514163528", "tg:1976509101", "tg:7091956351"],
groups: { "*": { requireMention: true } },
}
}
Either way is fine — the key is: don’t leave channels.telegram.groupPolicy as "open" if you mean allowlist.
One more thing: you pasted part of your bot token. It’s probably fine since it’s truncated, but if you ever pasted the full token anywhere public, rotate it in BotFather.
what if I want only specific people to be able to use the bot in some specific group instead of them having to use the bot in any group
Do two things:
- Don’t use
groups: { "*": … }(because*means “any group the bot is in is allowed”) - Add only the specific group IDs you want, and put a per‑group sender allowlist on each.
Step 1 — get the Telegram group chat ID
Easiest: send a message in the target group, then on the gateway host run:
openclaw logs --follow
Look for chat.id (it’ll look like -100...).
Step 2 — configure only that group + only those people
Example:
channels: {
telegram: {
groupPolicy: "allowlist", // safe default
groups: {
"-1003733326461": {
requireMention: true,
groupPolicy: "allowlist",
allowFrom: ["tg:6468789862", "tg:6514163528"], // only these users can trigger in THIS group
},
// other allowed groups go here; anything not listed is blocked
},
},
}
Notes:
allowFrom/groupAllowFrommust be numeric Telegram user IDs (yourtg:...format is perfect).- If a group isn’t listed under
channels.telegram.groups, it won’t trigger the bot at all (when your defaultgroupPolicyis allowlist).
If you paste the group’s chat.id (the -100… number) and the allowed user IDs, I can rewrite your exact block cleanly from what you have now.
17:23:20 error [tools] exec failed: elevated is not available right now (runtime=direct).
Failing gates: allowFrom (tools.elevated.allowFrom.<provider> / agents.list[].tools.elevated.allowFrom.<provider>)
Context: provider=telegram session=agent:main:telegram:direct:6468789862
Fix-it keys:
- tools.elevated.enabled
- tools.elevated.allowFrom.<provider>
- agents.list[].tools.elevated.enabled
- agents.list[].tools.elevated.allowFrom.<provider>
17:23:20 error [tools] exec failed: elevated is not available right now (runtime=direct).
Failing gates: allowFrom (tools.elevated.allowFrom.<provider> / agents.list[].tools.elevated.allowFrom.<provider>)
Context: provider=telegram session=agent:main:telegram:direct:6468789862
Fix-it keys: - tools.elevated.enabled
- tools.elevated.allowFrom.<provider>
- agents.list[].tools.elevated.enabled
- agents.list[].tools.elevated.allowFrom.<provider>
Log tail truncated (increase --max-bytes).
That error is expected if you haven’t explicitly allowlisted yourself for elevated exec on Telegram:
Failing gates: allowFrom (tools.elevated.allowFrom.<provider> …)
So tools.elevated.enabled is on, but your Telegram sender id isn’t in the elevated allowlist.
Fix (allowlist yourself for Telegram elevated)
Add your Telegram user id to tools.elevated.allowFrom.telegram.
Safest is to use the explicit id: matcher (so it matches SenderId exactly):
openclaw config set tools.elevated.allowFrom.telegram '["id:telegram:6468789862"]' --strict-json
openclaw gateway restart
(Replace 6468789862 with your actual Telegram user id; you already have it in your group allowlists.)
If you also want your alt accounts to have elevated, add them to that array too.
Docs: https://docs.openclaw.ai/tools/elevated#availability--allowlists
Security reminder
Don’t set this to "*" on Telegram. Elevated exec is basically “run commands on the gateway host”, so keep that allowlist tight.
always add the file I need to edit bro
If you want your bot to stop “guess-editing” and always load the file first, add a hard rule to your workspace AGENTS.md (that file is part of the system prompt).
Edit:
~/.openclaw/workspace/AGENTS.md
and add something like:
## File editing rules (important)
- If I ask you to edit a file, ALWAYS read the target file first.
- Never call `edit` unless you can copy/paste the exact current text (whitespace/newlines included).
- If an `edit` fails, re-read the file and try again (or rewrite the full file if it’s small).
- After editing, re-read the changed section to verify the result.
Then do /new in the chat (or restart the gateway) so the session picks up the updated instructions.
This directly prevents the “Could not find the exact text…” failures you were seeing.
Why do I always have to remind my open claw bot about some rules even though it's in the agent MD already
Because AGENTS.md is guidance, not a hard enforcement mechanism — and there are a few common reasons the model still “forgets” or ignores it:
1) Your AGENTS.md might be getting truncated
OpenClaw injects the workspace files every turn, but large files get truncated (per‑file and total caps). If the “rules” section is near the bottom, it may literally not be in the prompt.
Check in TUI:
/context list(shows what got injected + truncation)/context detail(if you want to inspect exactly what it’s seeing)
Fix: move the important rules to the top, and keep them short.
Docs: https://docs.openclaw.ai/concepts/system-prompt#workspace-bootstrap-injection
2) You may be in a different agent/session/workspace than you think
Rules in one agent workspace won’t apply to another. /status shows the current agent + session key.
3) Models don’t perfectly follow rules (and compaction doesn’t “teach”)
Even when the rules are present, LLMs sometimes optimize for “get task done” and violate process rules (like “read before edit”). If you need something to be guaranteed, use hard controls:
- tool policy (
tools.profile,tools.allow/deny) - group allowlists (
groupPolicy,groupAllowFrom) - elevated allowlists (
tools.elevated.allowFrom.*)
Practical pattern that works well
Put a very short “Hard rules” block at the very top of AGENTS.md, and rely on config/tool policy for anything safety-critical.
If you paste your /context list output, I can tell you immediately whether your AGENTS rules are being truncated or not injected.
Where do I edit this tool policy stuff
Tool policy lives in your gateway config (openclaw.json), not in AGENTS.md.
Where to edit it
- Find the active config file:
openclaw config file
(Usually ~/.openclaw/openclaw.json.)
- Edit it either:
- manually in an editor (it’s JSON5), or
- via the wizard:
openclaw configure - or via CLI
config set/unset(nice for small changes).
The knobs you were asking about
Global tool policy (applies to all agents unless overridden):
tools.profile(base allowlist)tools.allow/tools.denytools.fs.workspaceOnly
Per-agent overrides:
agents.list[<i>].tools.profileagents.list[<i>].tools.allow/.deny
Docs: https://docs.openclaw.ai/tools
Example: lock the whole bot to “messaging only”
openclaw config set tools.profile "messaging"
openclaw gateway restart
Example: allow tools generally, but block filesystem + exec everywhere
openclaw config set tools.deny '["group:fs","group:runtime"]' --strict-json
openclaw gateway restart
If you tell me what you want to allow (e.g. “Telegram DMs can use exec, groups cannot” vs “no exec at all”), I can point you to the exact config shape to get that behavior.
17:02:37 error [tools] read failed: EISDIR: illegal operation on a directory, read
17:02:37 error [tools] read failed: EISDIR: illegal operation on a directory, read
That error is just Unix being literal:
EISDIR = “that path is a directory, not a file”.
So something (the agent) tried to use the read tool on a folder path (common examples: memory/, .openclaw/, skills/, etc.). read can only open files.
What to do instead
- If you meant “read a specific file inside it” → give the full file path, e.g.
memory/2026-03-06.md. - If you meant “show me what’s in this folder” → list it with a shell command (via exec), e.g.:
- ask the bot to run:
ls -la <dir> - or in the TUI, use the local shell prefix:
! ls -la <dir>(runs on the TUI host)
- ask the bot to run:
If you tell me which path it was trying to read (or paste a couple lines above this log where the tool args are shown), I can suggest the exact command/path to use.
does the LLM model I use greatly affects my claw performance?
Yes — the model/provider you pick can change OpenClaw’s “feel” a lot.
It impacts performance in a few big ways:
- Latency per reply: some models/providers are simply slower to generate, and some have higher network overhead (proxies/routers add hops).
- Tool reliability: if the model doesn’t support tool calling (you hit this earlier with the
tool calling is not supportederrors), runs fail/redo/failover → huge perceived slowness. - Context window + prompt size: a bigger context window can be great, but if your session has grown to 10–20k tokens, every reply has to ship all that context → slower + more rate-limit pressure.
- Rate limits / quotas: a “fast” model on paper can still feel terrible if you’re constantly hitting 429s and backoff/cooldowns.
- Streaming support: some combos stream nicely (instant feedback), others only show the final message.
Practical approach:
- Use
/model listand pick a tool-capable model for your main agent. - Keep heartbeats low-frequency (or off) if you’re hitting rate limits.
- If you want speed + quality, use different agents/models for “fast chat” vs “heavy work”, or switch per session.
Docs: https://docs.openclaw.ai/concepts/models
For “which specific model is fastest today”, the best source is the Discord #models channel (things change constantly).
I want my bot to listen to all message from any one but only reply messages from me or allow list when tagged, is this configuration okay? " The OpenClaw Layer (Policy vs. Allowlist)
You will edit your openclaw.json to allow the bot to exist in the group "openly" but restrict who can actually trigger a response.
JSON
{
"channels": {
"telegram": {
"groupPolicy": "open", // Allows the bot to stay in any group and "hear" messages
"groups": {
"*": {
"requireMention": true // Recommended: Still requires @bot to prevent spam
}
},
"groupAllowFrom": [
"123456789", // Your Numeric User ID
"@YourUsername" // Your Handle
]
}
}
}
-
How this works in practice
For Non-Allowed Users: When a random user types a message or even @mentions the bot, OpenClaw will see the message (adding it to the conversation context) but the Authorization Layer will block the LLM from generating a reply.
For You: When you mention the bot, it checks groupAllowFrom, sees your ID, and processes the request normally.
Context Benefit: Because the bot is "listening" to everyone, when you finally ask it a question (e.g., "What were they just arguing about?"), it will have the full history of the other users' messages in its memory to give you an accurate answer."
That config is not doing what you think, and it’s also exactly what the security checker was warning about.
Why it’s not OK / not equivalent
groupPolicy: "open"means anyone in the group is authorized (so if they @mention the bot, they can trigger a run).- When access control does block a sender (in allowlist mode), OpenClaw generally drops the message before it reaches the agent, so you don’t get the “listen to everyone for context” benefit in a safe way.
groupAllowFromshould be numeric Telegram user IDs.@YourUsernamestyle entries are not reliable here (and are often ignored).
What you can do instead (supported + safe)
If your goal is: “bot can be in the group, but only specific people can trigger replies”:
{
channels: {
telegram: {
groupPolicy: "allowlist",
groups: {
"-100<groupId>": {
requireMention: true,
allowFrom: ["tg:6468789862", "tg:6514163528"] // only these users can trigger in this group
}
}
}
}
}
This gives you:
- bot can stay in the group
- it only replies when mentioned
- and only if the sender is allowlisted
About “listening to everyone but only replying to me”
That exact behavior (“ingest everyone’s messages into context, but never let them trigger runs”) is not something you get safely by setting groupPolicy=open—because open explicitly authorizes everyone to trigger runs.
If you really want “full group context”, the safe pattern is usually:
- keep the group agent/tooling extremely restricted (messaging-only / no exec/fs / no elevated), and
- accept that anyone can @mention it (since you’re intentionally letting it ingest untrusted text)
If you tell me which you care about more—(A) only you can trigger or (B) full group context—I’ll give you the cleanest config for that goal.
If you set groupPolicy: "allowlist" but leave groupAllowFrom empty, the bot will be "deaf"—it won't even see the messages in its logs. By setting it to "open", you ensure the bot's "ears" are working, while the "allowFrom" acts as its "voice" filter. """"""""""""""""" make sense?
The “ears vs voice” mental model is intuitive, but it’s not how OpenClaw’s group auth works today.
How it actually behaves:
-
groupPolicyis an authorization gate on inbound messages.groupPolicy: "allowlist"means: only senders in the allowlist are allowed to trigger/enter the authorized path. Others are treated as unauthorized (typically dropped/ignored with a skip reason).- If you leave the allowlist empty, you’ve effectively made the bot “deaf” in that context (and config validation usually rejects an empty allowlist anyway).
-
groupPolicy: "open"means: anyone in the group is authorized.- In this mode,
groupAllowFrom/ per-groupallowFromis not a “voice filter”. It doesn’t restrict who can trigger replies; it’s basically bypassed by the “open” policy.
- In this mode,
So: setting groupPolicy: "open" does not give you “listen to everyone but only reply to me”. It gives you “anyone can trigger the bot (when mention-gated).”
If what you want is a true “listen-only (store context) but only owner can trigger runs” mode, that’s a legit feature request — it’s not something you can safely express with open + allowlists right now. If you want, file it here: https://github.com/openclaw/openclaw/issues
Practical safe choice today:
- If you want only you to trigger →
groupPolicy: "allowlist"and allowlist just your IDs.
- If you want full group context → you must accept that others can trigger (and then lock tools down hard for that group/agent).
is there a way for me to listen to this messages without necessarily setting it with open claw probably thru an external script but still making use of the open claw telegram webhook?
Yes, but there’s an important Telegram constraint first:
Telegram Bot API constraint
A Telegram bot token can have only one webhook URL at a time.
So you can’t “simultaneously” have Telegram post updates to OpenClaw and to your own script unless you put something in the middle.
Option A (best if you want to keep the same bot/token): Webhook proxy / fan‑out
You set the Telegram webhook to your own tiny HTTP service, and that service:
- receives every update from Telegram (so you can “listen” / store / analyze)
- forwards the update to OpenClaw’s internal Telegram webhook listener
OpenClaw supports webhook mode (optional) via:
channels.telegram.webhookUrlchannels.telegram.webhookSecret(required)- (optional)
channels.telegram.webhookHost/webhookPort/webhookPath
Docs: https://docs.openclaw.ai/channels/telegram#long-polling-vs-webhook
Practical pattern:
- Public:
https://your-proxy.example.com/telegram-webhook← Telegram sends here - Private:
http://127.0.0.1:8787/telegram-webhook(OpenClaw listener) ← your proxy forwards here
Your proxy can also choose to only forward messages from you (so OpenClaw stays locked down), while still archiving everything else externally.
Option B (simpler operationally): Separate “listener bot”
Run a second Telegram bot (different token) whose only job is to log group messages somewhere (DB/file). Keep OpenClaw’s bot locked down with groupPolicy: "allowlist". When you want context, you ask OpenClaw and it fetches from your store (via a custom tool/plugin or a small HTTP endpoint).
Security / privacy warning
If you do ingest “everyone’s messages” into your assistant context, treat it as untrusted input (prompt injection risk). Lock down tools hard for that session/agent.
If you tell me which route you prefer (A proxy vs B second bot) and whether your Gateway is publicly reachable or only via a reverse proxy/Tailscale, I can outline the exact config wiring.
found this on the documentation website though, does it mean I can use open method and still use allow list "open (requires allowFrom to include "*")"
That doc line is easy to misread.
“open (requires allowFrom to include *)” does not mean “open + allowlist.”
It means: open to everyone, and the * requirement is just an explicit “yes I really want this to be public” opt‑in.
So:
-
DMs
dmPolicy: "open"⇒ anyone can DM-trigger the bot- and it requires
allowFromto contain"*"as a safety acknowledgment - adding specific IDs alongside
"*"doesn’t restrict anything (it’s still open)
-
Groups
groupPolicy: "open"⇒ anyone in the allowed groups can trigger the bot (subject torequireMentionif you keep that on)groupAllowFrom/ per-groupallowFromonly matter whengroupPolicy: "allowlist"
If you want “only me / allowlist can trigger in groups”, you want:
groupPolicy: "allowlist"groupAllowFrom: [...](or per-groupallowFrom: [...])- optionally
requireMention: true
What you’re trying to achieve (“listen to everyone, but only allowlisted people can trigger”) isn’t expressed by open + allowFrom; open is fundamentally “anyone can trigger.”
how does openclaw handles webhooks and get notificatins from telegram when tagged
OpenClaw can receive Telegram updates in two modes, and “being tagged” is just one kind of Telegram update it reacts to.
1) How it receives updates (notifications)
A) Long polling (default)
- OpenClaw runs a Telegram Bot API client (grammY) and repeatedly calls
getUpdates. - Telegram returns new messages/events, including group messages where the bot is mentioned (and other messages too, depending on Telegram privacy mode/admin settings).
- OpenClaw queues them and processes them per chat/thread in order.
B) Webhook mode (optional)
- Telegram pushes updates via HTTP POST to your
channels.telegram.webhookUrl. - OpenClaw starts a local listener (
webhookHost/webhookPort/webhookPath) and validateswebhookSecret. - If your gateway isn’t publicly reachable, you typically put a reverse proxy in front.
Docs: https://docs.openclaw.ai/channels/telegram#long-polling-vs-webhook
2) What happens “when tagged” in a group
Once OpenClaw receives the message update, it applies two gates:
-
Authorization gate (
groupPolicy+ allowlists)- If the sender isn’t allowed (e.g.
groupPolicy: "allowlist"and they’re not ingroupAllowFrom/ per-groupallowFrom), OpenClaw won’t run the agent.
- If the sender isn’t allowed (e.g.
-
Activation gate (
requireMention/ mention patterns)- If
requireMention: true, it only triggers when the message contains an@botusernamemention (or matches your configured mention patterns).
- If
If both pass, it routes to the right session key (group/topic), runs the agent, then sends the reply back via sendMessage (and optionally streaming preview if enabled).
If you tell me whether you’re using polling or webhook mode right now (and whether the bot is in Telegram Privacy Mode), I can tell you exactly what Telegram will or won’t deliver to OpenClaw in that group.
I didn't set anything so I guess it's using polling, not sure, how do I check
Easiest check: if you didn’t set a webhook URL, you’re on polling (that’s the default).
Check via OpenClaw config
On the gateway host:
openclaw config get channels.telegram.webhookUrl
- If it’s missing / empty → long polling
- If it’s set → webhook mode (and you should also have
channels.telegram.webhookSecretset)
Check via channel status + logs
openclaw channels status
openclaw logs --follow
On startup, the Telegram channel usually logs whether it’s running polling vs webhook, and channels status can surface config/runtime info.
(Optional) Check directly with Telegram
If you want to verify from Telegram’s side:
curl "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getWebhookInfo"
If url is empty, Telegram is not configured for a webhook → polling.
If you paste the output of openclaw config get channels.telegram.webhookUrl I’ll tell you definitively which mode you’re in.
◇
Gateway reachable.
- Telegram default: enabled, configured, running, in:1m ago, mode:polling, token:config
Tip: status --deep adds gateway health probes to status output (requires a reachable gateway).
Nice — that output answers it:
- mode:
polling✅ - Telegram is running and it last saw inbound traffic 1 minute ago
- token source is config
So Telegram delivery into the gateway is working; polling itself usually isn’t the reason for “slow replies” (that’s more often model latency / rate limits / the gateway being busy).
If you ever want to switch to webhook mode anyway, you’d set channels.telegram.webhookUrl + channels.telegram.webhookSecret and make the endpoint reachable from Telegram — but it’s optional.
which is more better though?
Neither is “always better” — it’s a tradeoff:
Polling (what you’re on now)
Pros
- Simplest setup (no public HTTP endpoint needed)
- Works behind NAT / firewalls / no reverse proxy
- Generally very reliable
Cons
- Constant
getUpdatesrequests (a bit more background traffic) - If your server’s outbound network/DNS is flaky, polling errors can cause gaps
Webhook
Pros
- Telegram pushes updates instantly (often slightly lower latency)
- Less constant polling traffic
Cons
- You must expose a stable public URL (usually HTTPS + reverse proxy)
- More moving parts (proxy, certs, firewall, webhook secret)
- Misconfig can silently drop updates if the endpoint isn’t reachable/healthy
My take: stick with polling unless you need webhook (public endpoint already exists and you want the cleaner push model). Polling is rarely the bottleneck — model latency / rate limits usually are.
If you tell me your gateway setup (VPS with public HTTPS? behind Tailscale only?), I can recommend one concretely.
so I need to constant send curl request to get any new updates?
No — you don’t have to do anything manually.
In polling mode, the OpenClaw gateway process is the thing that constantly calls Telegram’s getUpdates in the background (via grammY). As long as:
openclaw gateway/ the gateway service is running, and- Telegram channel is enabled/configured,
…it will automatically receive new messages and respond. No curl loop needed.
The only time you’d use curl getUpdates is for debugging or if you were writing your own Telegram bot from scratch.
yeah that's what I'm talking bout if for example I'm running my own script from scratch, it would need to keep pulling updates to stay updated, right? which means it would miss messages if it doesn't constantly pull or could miss messages few mins ago if there was a little time between each poll?
Yep — if you build your own Telegram bot “from scratch” and you choose polling, then something has to keep calling getUpdates.
But two important clarifications:
1) Polling doesn’t mean “spam requests every second”
You normally use long polling:
- Call
getUpdateswith anoffset(so you only get updates you haven’t processed yet) - Set a
timeout(Telegram holds the request open until there’s a new message or the timeout expires)
So you get near‑instant delivery with low request volume.
2) You don’t “miss” messages just because there’s a gap between polls
Telegram queues updates. If you poll every few minutes, you’ll still receive the messages that happened during the gap — they’ll just arrive late.
You only miss messages if:
- you mess up
offsethandling (ack too far / skip), or
- you leave it offline long enough that Telegram’s update backlog expires/overflows.
Critical gotcha (relevant to OpenClaw)
Only one poller should call getUpdates for a given bot token. If you run your own polling script at the same time as OpenClaw (same token), they’ll “steal” updates from each other and one of them will miss messages.
If you want a custom listener alongside OpenClaw, you’ll need webhook fan‑out (proxy) or a second bot token.
what does this do btw "{
// ... other OpenClaw configurations ...
"channels": {
"telegram": {
// ... existing telegram settings ...
"handlers": [
// ... other existing handlers ...
{
"type": "event",
"event": "message",
"actions": [
{
"type": "tool",
"tool": "write",
"args": {
"path": "/home/admin/.openclaw/workspace/telegram_messages_log.md",
"content": "{{ now | date('YYYY-MM-DD HH:mm:ss') }} | Sender: {{ message.sender.name }} ({{ message.sender.id }}) | Message ID: {{ message.id }}\n"
}
}
]
}
]
}
}
// ... rest of your openclaw.json ...
}
"
That snippet is trying to do: “on every Telegram message event, write a line to a log file” (timestamp + sender + message id). So basically a message logger.
Two important caveats:
- That exact
channels.telegram.handlersconfig is not a real OpenClaw config field (OpenClaw will reject unknown keys). - Even if it were, using the
writetool like that would typically overwrite the file unless the system explicitly supports append semantics.
The “real” OpenClaw way to do this: Hooks (message:received / message:preprocessed)
OpenClaw has a hooks system that can subscribe to:
message:received(very early)message:preprocessed(after transcription/link/image understanding)message:sent, etc.
Docs: https://docs.openclaw.ai/automation/hooks (see “Message Events”)
So if you want to log Telegram messages to a file, you’d create a hook that listens for message:received (or message:preprocessed) and appends to a file on disk.
High-level steps:
- Create a hook folder in your workspace:
<workspace>/hooks/telegram-logger/
- Add
HOOK.mdwithevents: ["message:received"] - Add
handler.tsthat checksevent.context.channelId === "telegram"andfs.appendFile(...) - Enable it:
openclaw hooks list openclaw hooks enable telegram-logger openclaw gateway restart
If you want, tell me:
- do you want to log only group messages, or DMs too?
- do you want the raw message text, or just metadata?
…and I’ll sketch the exact HOOK.md + handler.ts structure.
group chats but not necessariy those in the allow list, even those that are not, in the group that the bot is admin in
Yep — that’s doable with a message hook.
If the bot is admin in the group (or privacy mode is disabled), Telegram will deliver the group’s messages to the bot. Then a hook on message:received (or message:preprocessed) can log them even if your OpenClaw groupPolicy/allowlists would block replies.
Recommended: hook message:received, filter Telegram + group chats
Create:
<workspace>/hooks/telegram-group-logger/HOOK.md
---
name: telegram-group-logger
description: Log all Telegram group messages to a file.
metadata:
openclaw:
events: ["message:received"]
---
<workspace>/hooks/telegram-group-logger/handler.ts
import { appendFile, mkdir } from "node:fs/promises";
import { dirname } from "node:path";
const LOG_PATH = "/home/admin/.openclaw/workspace/telegram_group_log.md";
export default async function handler(event: any) {
// Only inbound messages
if (event?.type !== "message" || event?.action !== "received") return;
const ctx = event.context ?? {};
if (ctx.channelId !== "telegram") return;
// Telegram group chat ids are negative (e.g. -100...)
const conv = String(ctx.conversationId ?? "");
if (!conv.startsWith("-")) return;
const ts = event.timestamp?.toISOString?.() ?? new Date().toISOString();
const from = ctx.from ?? ctx.metadata?.senderId ?? "unknown";
const content = (ctx.content ?? "").replaceAll("\n", "\\n");
await mkdir(dirname(LOG_PATH), { recursive: true });
await appendFile(LOG_PATH, `${ts} | chat=${conv} | from=${from} | ${content}\n`, "utf8");
}
Enable it:
openclaw hooks list
openclaw hooks enable telegram-group-logger
openclaw gateway restart
Notes / gotchas
- This will only “hear everything” if Telegram actually sends everything to the bot (admin or privacy disabled).
- If you enable
groupPolicy: "allowlist", OpenClaw may still not reply, but the hook should still log messages atmessage:received. If you don’t see them in the log, it’s almost always Telegram privacy / bot not receiving those messages.
If you paste one real inbound Telegram log line (from openclaw logs --follow --json) I can confirm the exact fields (conversationId, from, etc.) so the hook matches your payload shape perfectly.
this might be what I need, Imma check it out in a sec