Troubleshooting
Troubleshooting
Quick Reference
| Issue | Solution |
|---|---|
| No DM reply | Check openclaw pairing list and approve pending requests |
| Silent in group chat | Verify mentionPatterns config — agent needs @mention |
| Agent can’t react/see WA group messages | Check requireMention per-group — true drops all non-@mention messages before they reach the session |
| Auth expired | openclaw models auth login --provider anthropic (OAuth) or setup-token --provider <p> for static-token providers |
| Gateway down | openclaw doctor --deep |
| Memory not indexing | openclaw memory index |
| Context full | /compact or /new |
| Channel disconnected | openclaw channels status --probe |
| Exec requires approval after update | Add tools.exec blocks to every agent in openclaw.json |
| Control UI blank over Tailscale | Add Tailscale hostname to controlUi.allowedOrigins |
*.ts.net not resolving locally | Check /etc/resolver/ts.net exists with nameserver 100.100.100.100 |
| Agent offline after idle (Deep Idle) | sudo pmset -a standby 0 networkoversleep 1 |
| Error messages spamming a chat channel | Check skills.entries for unresolved SecretRef objects — verify secrets.json has the referenced path, then openclaw secrets reload |
| WA “No active WhatsApp Web listener” but channel shows connected | Apply openclaw-patch-wa-listeners, clear jiti cache if EACCES, restart gateway |
| Duplicate replies on gpt-5.x Codex (Responses API) | Apply openclaw-patch-codex-dedup — strips directive tags from normalizeTextForComparison so commentary and final_answer phases with identical body text collapse to a single delivery. Tracking: openclaw/openclaw#59150, PR #59643 |
Universal fix: openclaw doctor --deep --yes
Skills SecretRef Errors
Error Messages Spamming a Chat Channel
Symptoms: Every inbound message in a channel triggers an error reply like Agent failed before reply: skills.entries.<name>.apiKey: unresolved SecretRef "file:secrets:/<path>". The error repeats on every message because web-auto-reply sends the crash message back to the originating channel.
Cause: A skills.entries entry contains a raw SecretRef object that the gateway failed to resolve to a string. The assertSecretInputResolved check throws during session initialization, before the model is even called. The model fallback chain retries all configured models, each failing identically, then web-auto-reply sends the error.
Fix:
- Verify the secret path exists in
~/.openclaw/secrets.json(e.g.,/skills/openai) - Reload secrets:
openclaw secrets reload - If the secret path is missing, add it to
secrets.jsonand reload - If the skill is no longer needed, remove the entry from
openclaw.json→skills.entries - Restart the gateway:
openclaw gateway restart
Prevention: Use a dedicated agent for channels with external visibility (WhatsApp, Slack) so gateway-global config regressions don’t spam error messages into those channels.
Known bug — embedded agent bypass (#49427): The WhatsApp channel’s embedded agent reads the raw config instead of the gateway’s resolved SecretRef runtime snapshot. Even when openclaw secrets reload succeeds and the main agent works fine, the WA embedded agent still hits unresolved refs. Workaround: replace file:secrets SecretRef objects with inline plaintext values in openclaw.json for any fields that the WA agent needs (e.g., tools.web.search.apiKey, talk.apiKey).
Plugin Config SecretRef Silent Failures
Plugin Appears Healthy But Authenticates With Garbage
Symptoms: A plugin’s tool calls return 401/403 or “invalid/expired token” from an external API, even though the bearer stored in ~/.openclaw/secrets.json is verified valid (direct curl with the same token succeeds). Rotating the secret doesn’t fix it. The plugin may appear to work for a long time before breaking — typically when a plist-env or .env shadow copy of the same variable is removed.
Cause: Unlike skills.entries SecretRefs (which the gateway resolves at startup and throws loudly on failure), the gateway does not auto-resolve SecretRef objects in plugins.entries.<id>.config. Plugins receive the raw {source, provider, id} object from pluginConfig and must resolve it themselves. A plugin that assumes config.token is already a string will spread the object into a child-process env — Node either stringifies it to "[object Object]" or drops the value silently. When a plist/env shadow provides a working token via ...process.env, the bug is masked indefinitely; removing the shadow exposes it immediately.
Fix (in the plugin source):
- Add
isSecretRef(v)/resolveSecretRef(ref)helpers covering all three source kinds:env(read fromprocess.env),execwithprovider: "keychain"(macOS Keychain viasecurity find-generic-password), andfilewithprovider: "secrets"(read~/.openclaw/secrets.jsonand follow theidas an RFC 6901 JSON pointer). - Resolve before using:
const token = typeof config.token === 'string' ? config.token : resolveSecretRef(config.token). - Log the outcome at
register()time so failures surface in the gateway log:console.log('[plugin-id] register: token source=..., resolved=yes|no').
Reference implementation: the Travel Hub openclaw plugin (openclaw/src/index.ts) — covers all three SecretRef source kinds including the file case. The parcel-cli plugin is sometimes cited as the reference, but it only handles env and exec and silently drops source: "file", which is the idiomatic openclaw pattern.
Detection: auth-health-check’s check_plugin_register_logs() scans the gateway log for register: ... resolved=no markers and alerts on any it finds. Plugins opt into this coverage by emitting the log line. Also flagged daily by the [Infra] Auth Health Check cron.
No Active WhatsApp Web Listener
Symptoms: Outbound messages fail with “No active WhatsApp Web listener (account: default)” but openclaw channels status --probe reports the channel as connected.
Cause: The Baileys listener is stored in a module-scoped Map(). After a WebSocket disconnect/reconnect, the extension may reload in a new jiti VM realm with its own Map instance. The delivery code reads from the old realm’s empty Map. This is the same class of bug as the webhook route registry isolation (#50208).
Fix:
- Apply the WA listener patch:
openclaw-patch-wa-listeners - If the patch fails with EACCES, clear stale jiti cache:
rm /tmp/jiti/plugin-sdk-thread-bindings-*.cjs - Restart the gateway:
openclaw gateway restart - Verify: send a test message with
--deliverand check logs for delivery failures
Prevention: Enable the channel health monitor (gateway.channelHealthCheckMinutes: 5) so stale channels auto-restart within 30 minutes.
iMessage
Health Check
Use the custom imsg build as the iMessage health source:
/Users/lobster/GitHub/imsg/bin/imsg status --jsonFor stuck agent turns, run:
bash openclaw-agents/lobster/scripts/stuck-session-watchdog.shStuck Session (iMessage Not Processing)
Symptoms: Agent stops responding to all iMessage DMs and group chats. The gateway is running, iMessage shows connected, but no messages are processed.
Cause: An exec tool call in a iMessage session hung indefinitely, blocking the session lane.
Fix:
- Run the stuck-session watchdog to confirm:
bash openclaw-agents/lobster/scripts/stuck-session-watchdog.sh - If stuck runs are detected, restart the gateway:
openclaw gateway restart - Verify
agents.defaults.timeoutSecondsis set to 180 (not the old 600s default) to prevent recurrence
Messages.app AppleScript Hung
Symptoms: Agent can receive messages (webhooks arrive) but replies fail silently or time out.
Cause: Messages.app’s AppleScript interface becomes unresponsive (internal state corruption, large iCloud sync, etc.).
Fix:
# Force kill and relaunch Messages.apppkill -9 Messagessleep 3open -a MessagesPrivate API Disconnected
Symptoms: Typing indicators, read receipts, and tapbacks stop working. Messages still send via AppleScript fallback.
Cause: The iMessage Private API helper disconnected. This requires SIP to be disabled on the host Mac.
Fix: Run imsg launch --json, then verify with imsg status --json. If SIP was re-enabled (e.g., after a macOS update), disable it again per the iMessage Private API docs.
Not Receiving Messages
Run imsg status --json, then inspect ~/.openclaw/logs/openclaw.log for gateway/channels/imessage errors.
DMs Not Getting Responses
If dmPolicy is pairing (the default), senders need a pairing code. Switch to allowlist:
openclaw config set channels.imessage.dmPolicy allowlistopenclaw config set 'channels.imessage.allowFrom' '["+1XXXXXXXXXX","+1XXXXXXXXXX"]'openclaw gateway restartURL Rich-Link Previews Not Reaching the Agent (Split-Send)
When a user pastes a URL into iMessage and Apple auto-converts it into a rich-link preview card, iMessage often forwards two distinct webhook events — the command text (e.g., Dump) first, then the URL-balloon later — each with its own messageId and no associatedMessageGuid tying them together. Without coalescing, the agent receives two separate turns and sees only "Dump" on the first, so URL-trigger skills complain that no URL was provided.
Fix: Opt into same-sender DM coalescing. Requires OpenClaw v2026.4.22+ (the v2026.4.21 npm bundle was cut missing PR #69258 — the git tag contains it but the published dist does not).
// ~/.openclaw/openclaw.json (and the repo copy)"channels": { "imessage": { // ...existing fields... "coalesceSameSenderDms": true }}Restart the gateway. Verify with grep coalesceSameSenderDms ~/.openclaw/openclaw.json — if the flag disappears on restart, the installed gateway doesn’t know the key (upgrade first). Enabling the flag widens the default inbound iMessage debounce from 500 ms to 2500 ms so Apple’s 0.8–2.0 s split-send cadence falls inside one debounce bucket. Group chats continue to key per-message, so multi-user turn structure is preserved.
Workaround until upgraded: send URLs as plain text (type them, don’t paste a preview card) or pair the URL with an image so the attachment payload travels alongside the text.
Group Messages Not Reaching Agent
Symptoms: Channel health shows WhatsApp connected and linked, but group messages never appear in the agent session. No errors in gateway logs at info level.
Cause: groupAllowFrom contains a group JID (e.g., [email protected]) instead of E.164 phone numbers. The gateway matches each sender’s phone number against this list — a group JID will never match, so every message is silently blocked. The blocking is logged at verbose level only.
Fix: Replace the group JID with sender phone numbers:
"groupAllowFrom": [ "+1XXXXXXXXXX", "+1XXXXXXXXXX"]Or use ["*"] to allow all group participants. Then restart the gateway.
Verify: openclaw channels status --probe should show the channel as connected with the correct dm: and allow: values.
WhatsApp Web Socket Cycling
Symptoms: Frequent stale-socket restarts in logs (every 30-40 minutes), occasional DNS failures for web.whatsapp.com, corrupted creds.json.
Possible causes: Network instability during Mac sleep, contested session from another WhatsApp Web device, or stale session state.
Fix: Check if another device is linked to the same WhatsApp account. Consider wacli auth status for a separate diagnostic view. Persistent cycling may require re-linking: openclaw channels login.
Tailscale
DNS / Serve Not Working
Use the Homebrew CLI (brew install tailscale), not the GUI app (brew install --cask tailscale). The GUI app is sandboxed and can’t properly configure macOS DNS or Tailscale Serve.
MagicDNS Not Resolving on the Agent Mac
Symptoms: curl https://your-agent.tailnet.ts.net fails with “Could not resolve host” on the agent Mac itself, but works from other Tailscale devices.
Cause: Homebrew-installed tailscaled creates /etc/resolver/search.tailscale but not /etc/resolver/ts.net. macOS split-DNS uses the filename as the domain — search.tailscale only handles *.search.tailscale, not *.ts.net.
Fix: Install the com.lobster.tailscale-dns LaunchDaemon (in config/):
sudo cp config/com.lobster.tailscale-dns.plist /Library/LaunchDaemons/sudo launchctl load /Library/LaunchDaemons/com.lobster.tailscale-dns.plistThis creates /etc/resolver/ts.net with nameserver 100.100.100.100 and watches /etc/resolver/ to recreate it if wiped by Tailscale updates.
Stale Tailscale Shim
If you previously installed the Mac App Store version, a shim script at /usr/local/bin/tailscale may shadow the Homebrew binary:
sudo rm /usr/local/bin/tailscaleSSH Hangs or Timeouts
Verify Tailscale SSH is enabled:
tailscale statusIf SSH isn’t shown, re-enable:
sudo tailscale up --sshMCP Servers
Tools Not Working
MCP servers accessed via CLI clients (like mcporter) require the exec tool to be in the agent’s alsoAllow list. Check:
"tools": { "profile": "minimal", "alsoAllow": ["exec", ...] // exec must be here}Mail Agent Delegation Timeout
Symptoms: Agent says it “timed out waiting for the reply” from the mail agent, but the mail agent’s summary appears in the gateway logs shortly after.
Cause: sessions_send defaults to a 30-second timeout when timeoutSeconds is omitted. The mail agent typically needs 25–35 seconds for a search-then-read flow (7s gateway routing + two Fastmail API calls + LLM summary generation). If total wall time exceeds 30s, the caller gets "status": "timeout" even though the mail agent finishes moments later.
Fix: Always pass timeoutSeconds: 60 in sessions_send calls to the mail agent. This is documented in TOOLS.md under the mail delegation section.
{ "sessionKey": "agent:lobster-mail:main", "agentId": "lobster-mail", "message": "Summarize the email about [topic]", "timeoutSeconds": 60}Project Config Shadowing
mcporter reads ./config/mcporter.json relative to the agent’s working directory as a “project config” that shadows the system config. If MCP calls are failing, check that the project config is empty:
mcporter config listThe system config at ~/.mcporter/mcporter.json should be the single source of truth.
Token Expired
Re-authenticate at the server’s /get-token endpoint, copy the new Bearer token, and update ~/.mcporter/mcporter.json.
Apple PIM
CLI Hangs When Called by OpenClaw
The first invocation of each PIM CLI triggers a macOS TCC permission prompt. This prompt can’t display in a non-interactive context (like when spawned by OpenClaw). Fix by running each CLI manually from Terminal first:
~/.local/bin/calendar-cli list~/.local/bin/reminder-cli lists~/.local/bin/contacts-cli listWrong Agent Seeing Wrong Calendars
Each agent has a workspace-local apple-pim/config.json that the plugin reads automatically. Check that the config exists and has the correct blocklists:
cat ~/.openclaw/agents/<agent-id>/workspace/apple-pim/config.jsonOpenClaw Gateway
Gateway Won’t Start
# Check for port conflictslsof -i :18789
# Run diagnosticsopenclaw doctor --deep
# Check logsopenclaw logs --tail 100Sessions Not Resetting
Sessions reset daily at 4 AM and after 2 hours of inactivity. Force a reset:
# Via iMessage/new
# Or via CLIopenclaw session reset --agent <agent-id>Auth Error for Non-Default Agent
openclaw onboard only configures auth for the default agent. Copy the auth profile to other agents:
cp ~/.openclaw/agents/main-agent/agent/auth-profiles.json \ ~/.openclaw/agents/other-agent/agent/auth-profiles.jsonConfiguration
Permissions Reset After jq Edit
jq writes to a temp file then mvs it, resetting permissions to 644. Always restore:
chmod 600 ~/.openclaw/openclaw.jsonTool Config Using Wrong Keys
OpenClaw uses profile + alsoAllow / deny, NOT allow / deny. Using allow causes agents to lose all tools beyond the minimal profile.
// CORRECT"tools": { "profile": "minimal", "alsoAllow": ["exec", "web_search"], "deny": ["write", "edit"]}
// WRONG — "allow" is not recognized"tools": { "allow": ["exec", "web_search"]}Exec Approvals Not Working
Check that:
- The
exec-approvals.jsonfile exists at~/.openclaw/exec-approvals.json - The agent ID in the file matches the agent’s actual ID
- The command paths in the allowlist match the actual binary locations
autoAllowSkillsis set tofalsefor restricted agents
All Exec Commands Require Approval After Update
Symptoms: After upgrading OpenClaw, all exec commands (even echo hello) require manual approval and time out at 120 seconds.
Cause: OpenClaw v2026.2.22+ properly resolves per-agent tools.exec config in openclaw.json. Agents without an explicit tools.exec block fall through to gateway defaults (security: "allowlist", ask: "on-miss").
Fix: Add explicit tools.exec blocks to every agent in openclaw.json:
// Main agent (allowlist with owner approval for unlisted commands)"tools": { "exec": { "security": "allowlist", "ask": "on-miss" }}
// Restricted agents (same structure, smaller allowlist in exec-approvals.json)"tools": { "exec": { "security": "allowlist", "ask": "on-miss" }}Also add safeBinTrustedDirs — v2026.2.22+ stopped trusting PATH-derived directories:
"tools": { "exec": { "safeBinTrustedDirs": [ "/path/to/.local/bin", "/opt/homebrew/bin" ] }}Then restart: openclaw daemon restart
Important: Setting these values in exec-approvals.json alone is NOT enough — that file controls node host exec, not gateway exec. You must configure both files. See Security Model for details.
Debug commands:
# Check gateway runtime exec config (not just local file)openclaw approvals get --gateway
# Push local config to running gatewayopenclaw approvals set --file ~/.openclaw/exec-approvals.json --gatewayControl UI Not Loading Over Tailscale
Symptoms: Tailscale Serve is running and the gateway is accessible, but the Control UI shows a blank page or “origin not allowed” errors in the browser console.
Cause: The gateway rejects WebSocket connections from origins not in the allowedOrigins list.
Fix: Add your Tailscale hostname to controlUi.allowedOrigins and enable auth.allowTailscale in openclaw.json:
"controlUi": { "enabled": true, "allowedOrigins": ["https://your-agent.your-tailnet.ts.net"]},"auth": { "mode": "token", "token": "${OPENCLAW_GATEWAY_TOKEN}", "allowTailscale": true}Then restart the gateway. Do NOT change bind to "tailnet" — this breaks iMessage webhooks and Tailscale Serve.
If prompted for device pairing, approve via openclaw devices list and openclaw devices approve <requestId>.