Skip to content

Troubleshooting

Troubleshooting

Quick Reference

IssueSolution
No DM replyCheck openclaw pairing list and approve pending requests
Silent in group chatVerify mentionPatterns config — agent needs @mention
Agent can’t react/see WA group messagesCheck requireMention per-group — true drops all non-@mention messages before they reach the session
Auth expiredopenclaw models auth login --provider anthropic (OAuth) or setup-token --provider <p> for static-token providers
Gateway downopenclaw doctor --deep
Memory not indexingopenclaw memory index
Context full/compact or /new
Channel disconnectedopenclaw channels status --probe
Exec requires approval after updateAdd tools.exec blocks to every agent in openclaw.json
Control UI blank over TailscaleAdd Tailscale hostname to controlUi.allowedOrigins
*.ts.net not resolving locallyCheck /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:

  1. Verify the secret path exists in ~/.openclaw/secrets.json (e.g., /skills/openai)
  2. Reload secrets: openclaw secrets reload
  3. If the secret path is missing, add it to secrets.json and reload
  4. If the skill is no longer needed, remove the entry from openclaw.jsonskills.entries
  5. 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):

  1. Add isSecretRef(v) / resolveSecretRef(ref) helpers covering all three source kinds: env (read from process.env), exec with provider: "keychain" (macOS Keychain via security find-generic-password), and file with provider: "secrets" (read ~/.openclaw/secrets.json and follow the id as an RFC 6901 JSON pointer).
  2. Resolve before using: const token = typeof config.token === 'string' ? config.token : resolveSecretRef(config.token).
  3. 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.

WhatsApp

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:

  1. Apply the WA listener patch: openclaw-patch-wa-listeners
  2. If the patch fails with EACCES, clear stale jiti cache: rm /tmp/jiti/plugin-sdk-thread-bindings-*.cjs
  3. Restart the gateway: openclaw gateway restart
  4. Verify: send a test message with --deliver and 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:

Terminal window
/Users/lobster/GitHub/imsg/bin/imsg status --json

For stuck agent turns, run:

Terminal window
bash openclaw-agents/lobster/scripts/stuck-session-watchdog.sh

Stuck 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:

  1. Run the stuck-session watchdog to confirm: bash openclaw-agents/lobster/scripts/stuck-session-watchdog.sh
  2. If stuck runs are detected, restart the gateway: openclaw gateway restart
  3. Verify agents.defaults.timeoutSeconds is 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:

Terminal window
# Force kill and relaunch Messages.app
pkill -9 Messages
sleep 3
open -a Messages

Private 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:

Terminal window
openclaw config set channels.imessage.dmPolicy allowlist
openclaw config set 'channels.imessage.allowFrom' '["+1XXXXXXXXXX","+1XXXXXXXXXX"]'
openclaw gateway restart

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.

WhatsApp

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/):

Terminal window
sudo cp config/com.lobster.tailscale-dns.plist /Library/LaunchDaemons/
sudo launchctl load /Library/LaunchDaemons/com.lobster.tailscale-dns.plist

This 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:

Terminal window
sudo rm /usr/local/bin/tailscale

SSH Hangs or Timeouts

Verify Tailscale SSH is enabled:

Terminal window
tailscale status

If SSH isn’t shown, re-enable:

Terminal window
sudo tailscale up --ssh

MCP 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:

Terminal window
mcporter config list

The 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:

Terminal window
~/.local/bin/calendar-cli list
~/.local/bin/reminder-cli lists
~/.local/bin/contacts-cli list

Wrong 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:

Terminal window
cat ~/.openclaw/agents/<agent-id>/workspace/apple-pim/config.json

OpenClaw Gateway

Gateway Won’t Start

Terminal window
# Check for port conflicts
lsof -i :18789
# Run diagnostics
openclaw doctor --deep
# Check logs
openclaw logs --tail 100

Sessions Not Resetting

Sessions reset daily at 4 AM and after 2 hours of inactivity. Force a reset:

Terminal window
# Via iMessage
/new
# Or via CLI
openclaw 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:

Terminal window
cp ~/.openclaw/agents/main-agent/agent/auth-profiles.json \
~/.openclaw/agents/other-agent/agent/auth-profiles.json

Configuration

Permissions Reset After jq Edit

jq writes to a temp file then mvs it, resetting permissions to 644. Always restore:

Terminal window
chmod 600 ~/.openclaw/openclaw.json

Tool 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:

  1. The exec-approvals.json file exists at ~/.openclaw/exec-approvals.json
  2. The agent ID in the file matches the agent’s actual ID
  3. The command paths in the allowlist match the actual binary locations
  4. autoAllowSkills is set to false for 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:

Terminal window
# Check gateway runtime exec config (not just local file)
openclaw approvals get --gateway
# Push local config to running gateway
openclaw approvals set --file ~/.openclaw/exec-approvals.json --gateway

Control 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>.