# Openclaw Ops

> Configuration gotchas and operational patterns for OpenClaw multi-agent systems

- Skill: `chris2ao/openclaw-ops` (Agent Skill)
- Install (CLI): `npx skillmds@latest add chris2ao/openclaw-ops`
- Raw SKILL.md: https://api.skillmd.com/api/skills/chris2ao/openclaw-ops/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: chris2ao (https://skillmd.com/u/chris2ao)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/chris2ao/openclaw-ops

---


# /openclaw-ops - OpenClaw Configuration and Operations

Activate when configuring OpenClaw, parsing its status output, setting up Telegram integrations, or troubleshooting agent memory search. These are non-obvious config gotchas that cost time to rediscover.

## Steps

### 1. Verify Config After openclaw doctor --fix

`openclaw doctor --fix` applies schema normalization which reverts manual customizations to defaults. After every doctor invocation, check these settings:

```bash
openclaw doctor --fix
# Always re-verify after:
jq '.agents.defaults.groupPolicy' ~/.openclaw/openclaw.json
# If reverted to "allowlist", restore:
jq '.agents.defaults.groupPolicy = "open"' ~/.openclaw/openclaw.json > tmp && mv tmp ~/.openclaw/openclaw.json
```

Reapply any custom values (groupPolicy, telegram bindings, plugin enables) after each doctor run.

### 2. memorySearch Config Nesting

Place `memorySearch` under `agents.defaults`, not at top level or at `agents`:

```json
// Correct
{ "agents": { "defaults": { "memorySearch": { ... } } } }

// Wrong (top-level, silently ignored)
{ "memorySearch": { ... } }

// Wrong (wrong nesting level)
{ "agents": { "memorySearch": { ... } } }
```

### 3. Status JSON Agent Nesting

When parsing `openclaw status --json`, agents are at `result.agents.agents` (an array), not `result.agents`:

```ts
const status = JSON.parse(await execFileAsync('openclaw', ['status', '--json']))
const agentList = status.agents.agents  // array
// NOT: status.agents (object with nested structure)
```

### 4. Telegram Long-Polling Watchdog

Telegram long-polling connections die after ~8 minutes idle. Add a cron watchdog to auto-restart the gateway:

```bash
# Add to crontab: crontab -e
*/5 * * * * ~/.claude/scripts/telegram-watchdog.sh >> ~/.openclaw/logs/watchdog.log 2>&1
```

The watchdog script should:
1. Check the last activity timestamp from the gateway log
2. If stalled more than 10 minutes, run `launchctl bootout` then `launchctl bootstrap` (not `stop/start`)
3. Log the restart event with timestamp

### 5. Embedded Gateway Token Warning on macOS

Doctor flags `Gateway service embeds OPENCLAW_GATEWAY_TOKEN and should be reinstalled.` and recommends `openclaw gateway install --force`. **On macOS this does not clear the warning** (verified in 2026.4.23). The audit at `dist/service-audit-*.js` skips the warning only when `environmentValueSources.OPENCLAW_GATEWAY_TOKEN === "file"`; the systemd module sets that field, but the launchd module has no file-source support. There is no `--token-file` option (only `--password-file` for password auth).

Practical workaround: tighten the plist permissions so they match `~/.openclaw/.env`.

```bash
chmod 600 ~/Library/LaunchAgents/ai.openclaw.gateway.plist
```

Token in `~/.openclaw/openclaw.json` is a `${OPENCLAW_GATEWAY_TOKEN}` placeholder; the actual value lives in `~/.openclaw/.env` (mode 600). After chmod, exposure matches. Doctor will keep firing the warning because it audits plist content, not perms. Treat as a known macOS-only false positive until upstream adds launchd file-source support.

### 6. Session Store Maintenance

Two distinct kinds of session debt that doctor reports separately:

```bash
# Entries pointing to missing transcript files (.jsonl deleted out from under sessions.json)
openclaw sessions cleanup --enforce --fix-missing \
  --store ~/.openclaw/agents/<agent>/sessions/sessions.json

# Orphan transcript files (.jsonl on disk not referenced by sessions.json) — archive manually
cd ~/.openclaw/agents/<agent>/sessions
TS=$(date -u +%Y-%m-%dT%H-%M-%S.000Z)
for f in <orphan-uuid>.jsonl; do mv "$f" "$f.deleted.$TS"; done
```

`--fix-missing` is needed in addition to `--enforce`; without it, `cleanup` only handles age/count retention. Renaming to `*.deleted.<ts>` matches the pattern doctor itself uses, keeps the data recoverable, and clears the orphan count.

### 7. OpenClaw Sessions Cache the System Prompt

Sessions are sticky: `sessions.json` caches `systemSent: true` once a session has sent its system prompt. Editing `SOUL.md` or `AGENTS.md` does not take effect until that session is cleared:

1. Send `/restart` to the agent via Telegram DM
2. Only then test the updated configuration

Without this, you'll see the old cached behavior and incorrectly conclude your edit didn't work.

### 8. Guard Memory File Reads in Agent Configuration

If `AGENTS.md` unconditionally reads a date-stamped memory file (e.g. `memory/2026-05-09.md`) that doesn't exist yet, the resulting ENOENT drives the model into tool-error-recovery: it treats the error as transcript corruption and emits a duplicate canned fallback greeting alongside the legitimate reply. Guard the read with an existence check (`ls`) first, or provide a fallback value, rather than reading unconditionally.

## Vector 0.40 VRL Gotchas

These gotchas apply when authoring VRL transforms for Vector 0.40 pipelines (SIEM log-lake and similar):

1. **Regex named-capture type**: named captures return `string | null` (not `string`). Always cast: `string!(.captures.name)` before passing to `contains()`, `starts_with()`, etc. The linter error is `E110 unexpected type`.

2. **`.timestamp` is non-nullable**: `.timestamp` on an event is always present; `?? now()` fallback is a no-op and the VRL parser rejects it. Access `.timestamp` directly.

3. **`to_int(null)` returns 0, not error**: `to_int(null)` silently returns `0` instead of raising. If you need to distinguish "field missing" from "field is 0", check `exists(.field)` first.

4. **No first-class functions (closures)**: VRL has no lambda/closure support. Map and filter operations must be written as loops with explicit `push`/mutation.

5. **HTTP request headers format**: use the `[[sources.foo.headers]]` TOML array-of-tables format, not inline `headers = {}`. Inline format silently drops headers.

6. **`encoding.codec` rejected in sinks**: `encoding.codec` is not a valid key in most sink encoding blocks. Use `encoding.only_fields`, `encoding.except_fields`, and `encoding.timestamp_format` only.

7. **Disk buffer minimum size**: Vector disk buffer requires at least 268,435,488 bytes (256 MiB + 32 bytes). Values below this crash Vector at startup with a confusing "invalid configuration" error. Set `max_size = 268435456` (256 MiB) as the practical floor -- Vector rounds up the 32-byte header internally.

8. **syslog source emits 12 non-schema fields**: the `syslog` source emits `appname`, `facility`, `hostname`, `message`, `msgid`, `procid`, `severity`, `source_ip`, `timestamp`, `version`, plus `structured_data` and `source_type`. If your downstream sink expects a clean schema, add `encoding.only_fields = ["appname", "facility", "hostname", "message", "severity", "timestamp"]` (adjust to your schema) to avoid sending surprise fields to ClickHouse/Kafka.

## Source Instincts

- `openclaw-doctor-revert`: "when running openclaw doctor after manual config edits"
- `openclaw-memorysearch`: "when configuring memory search in OpenClaw"
- `openclaw-agents-nesting`: "when parsing openclaw status --json output"
- `telegram-polling-watchdog`: "when integrating Telegram with long-polling"
- `openclaw-launchd-token-gap`: "when doctor reports embedded OPENCLAW_GATEWAY_TOKEN on macOS"
- `openclaw-session-cleanup`: "when doctor reports orphan transcripts or missing-transcript entries"

