openclaw - ✅(Solved) Fix [Bug]: Extension plugins silently skipped by gateway loader in v5.3+ (loads in CLI process, not in long-running daemon) [2 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#78196Fetched 2026-05-06 06:16:00
View on GitHub
Comments
1
Participants
2
Timeline
5
Reactions
2
Author
Timeline (top)
cross-referenced ×2labeled ×2commented ×1

Starting with v2026.5.3, the long-running gateway daemon silently fails to load extension plugins from plugins.load.paths, even when every config key checks out. Bundled plugins (memory-core) load normally; extensions under /home/node/.openclaw/extensions/<name>/ are skipped with no error, no warning, no log line. The same plugin loads cleanly inside short-lived CLI processes (openclaw plugins inspect, openclaw plugins doctor) — those processes emit the plugin's register banner every time. So the failure is gateway-daemon-specific, not a packaging, manifest, or syntax problem.

Confirmed on two unrelated extensions in the same install:

  • claw-guard v3.1.1 (security audit hook — registers before_tool_call)
  • approval-formatter v1.0.0 (Discord/Telegram approval message formatter)

Both v2026.5.3-1 and v2026.5.4 are affected. Promote from v5.3-1 → v5.4 did not resolve. None of the v5.4 release-note candidates (bundledDiscovery: "compat", plugins.entries.<name>.origin: "config", doctor --fix, plugin reinstall, registry refresh) restored extension loading.

The user-visible impact is silent at the operational layer: tool calls still execute, crons return status: ok. The security layer (ClawGuard before_tool_call hook) never fires, so audit.jsonl freezes and tool-call scanning is offline. We only caught it because a separate cron monitors audit.jsonl mtime and alerted to Discord every 30 minutes after stagnation crossed threshold.

Error Message

Starting with v2026.5.3, the long-running gateway daemon silently fails to load extension plugins from plugins.load.paths, even when every config key checks out. Bundled plugins (memory-core) load normally; extensions under /home/node/.openclaw/extensions/<name>/ are skipped with no error, no warning, no log line. The same plugin loads cleanly inside short-lived CLI processes (openclaw plugins inspect, openclaw plugins doctor) — those processes emit the plugin's register banner every time. So the failure is gateway-daemon-specific, not a packaging, manifest, or syntax problem.

  • Actual: only [gateway] http server listening (1 plugin: memory-core; X.Xs). Zero output for the extension. No error. No warning. The structured log at /tmp/openclaw/openclaw-YYYY-MM-DD.log also contains nothing about the extension load attempt during the boot window. At gateway boot, every entry in plugins.load.paths should be evaluated. Each one that is plugins.allow-listed and plugins.entries.<name>.enabled = true should be loaded into the long-running daemon, its register() hook invoked, and a load/register/error line written to either docker logs openclaw or /tmp/openclaw/openclaw-*.log. Subsequent agent tool calls inside cron runs should fire the registered hooks (before_tool_call, etc.) so plugins like claw-guard can append to their audit log. If a plugin path is rejected (signature, version mismatch, schema, capability), the gateway should emit a clear error or warning naming the path and the reason — currently it emits nothing at all. Gateway boot loads only the bundled memory-core plugin. Extension paths are silently skipped — no register banner, no error, no warning. The CLI-side view (openclaw plugins inspect, openclaw plugins doctor) keeps reporting Status: loaded and prints the register banner because each CLI invocation re-imports the plugin in its own short-lived Node process, which works fine. The long-running gateway daemon, which is the process that needs the hook wired to handle tool calls, never registers the extension.

Root Cause

The user-visible impact is silent at the operational layer: tool calls still execute, crons return status: ok. The security layer (ClawGuard before_tool_call hook) never fires, so audit.jsonl freezes and tool-call scanning is offline. We only caught it because a separate cron monitors audit.jsonl mtime and alerted to Discord every 30 minutes after stagnation crossed threshold.

Fix Action

Fix / Workaround

Not relevant to this bug. The gateway never reaches the model/provider step — extensions fail to load before any tool call dispatch. Same failure occurs with any model selected.

The Config overwrite line is also missing on broken boots, even when openclaw.json's sha256 visibly changed via openclaw config patch. Whatever code path detects the config delta and triggers extension load is not firing.

Hot reload "applied" — but follow-up cron run still produced no audit growth. The hot-reload event seems to update the config snapshot without actually re-binding extension hooks into the dispatch path.

PR fix notes

PR #78203: fix: start explicit legacy hook plugins

Description (problem / solution / changelog)

Fixes #78196.

Summary

  • Treat explicitly enabled non-bundled plugins as gateway startup hook intent when they lack newer activation.onCapabilities: ["hook"] metadata.
  • Keep the existing allow/deny/global-enabled activation checks, so this restores legacy external hook compatibility without ambient-loading merely allowlisted plugins.
  • Add startup planner regression coverage for explicit legacy external hook plugins and the ambient-load negative case.

Verification

  • git diff --check
  • PATH="/tmp/openclaw-pnpm-shim:$PATH" pnpm exec oxfmt --check src/plugins/gateway-startup-plugin-ids.ts src/plugins/channel-plugin-ids.test.ts
  • Targeted tests attempted: PATH="/tmp/openclaw-pnpm-shim:$PATH" pnpm test src/plugins/channel-plugin-ids.test.ts src/gateway/server-plugins.test.ts — blocked before tests by missing local @openclaw/fs-safe/config import from src/infra/fs-safe-defaults.ts.
  • PATH="/tmp/openclaw-pnpm-shim:$PATH" node scripts/check-changed.mjs — passed conflict/changelog/extension wildcard/duplicate lanes; failed unrelated existing core typecheck diagnostics including missing @openclaw/fs-safe/* and strictness errors outside this patch.

Notes: pnpm install --offline --ignore-scripts in the /tmp worktree was attempted to repair missing deps but hit /tmp ENOSPC, so subsequent checks used the main repo's existing node_modules symlink.

Changed files

  • src/plugins/channel-plugin-ids.test.ts (modified, +31/-0)
  • src/plugins/gateway-startup-plugin-ids.ts (modified, +5/-3)

PR #78229: fix(plugins): start explicit non-bundled hook plugins lacking onCapabilities

Description (problem / solution / changelog)

Fixes #78196.

Root cause

v5.3 tightened hasHookRuntimeStartupIntent to require either a manifest activation.onCapabilities: [\"hook\"] declaration or explicit hooks.* policy keys. Pre-v5.3 external extensions (claw-guard, approval-formatter, …) shipped before that metadata existed and were activated solely via plugins.entries.<id>.enabled = true plus plugins.allow. After v5.3, those plugins quietly fall through canStartExplicitHookPlugin because their startup intent is empty, so the gateway daemon skips them with no error or warning.

The CLI-side path (openclaw plugins inspect) keeps reporting them as Status: loaded because each CLI invocation re-imports the plugin in its own short-lived Node process — that path is fine. The long-lived gateway daemon, which is the process that needs the hook wired to handle tool calls, is the one that silently skips them.

Net effect for the reporter: before_tool_call from claw-guard never fires, audit.jsonl stops advancing, and tool calls run unaudited.

What the fix does

Treat any non-bundled plugin that the user has explicitly enabled (activationSourcePlugins.entries[pluginId]?.enabled === true) as having legacy hook startup intent. Bundled plugins keep their manifest-driven activation. Merely allowlisted (but not explicitly enabled) plugins remain ambient and are still skipped — the existing allow-vs-load distinction is preserved.

Tests

  • Added two regression cases in channel-plugin-ids.test.ts reusing the existing external-hook-policy fixture (origin: global, no manifest hook capability, no explicit hook policy):
    • explicitly enabled + allowed => loads at startup
    • allowlisted only, no entry => still does NOT load (negative case)

Real behavior proof

Unit tests only. Local vitest blocked before tests by the existing @openclaw/fs-safe/config infra gap. The behavior change is a single new short-circuit in hasHookRuntimeStartupIntent and is exercised by the new positive/negative cases.

Changed files

  • src/plugins/channel-plugin-ids.test.ts (modified, +29/-0)
  • src/plugins/gateway-startup-plugin-ids.ts (modified, +16/-3)

Code Example

docker exec openclaw openclaw config get plugins.allow            # must include <name>
   docker exec openclaw openclaw config get plugins.load.paths       # must include extension dir
   docker exec openclaw openclaw config get plugins.entries.<name>   # { "enabled": true }

---

docker exec openclaw openclaw plugins inspect <name>
   # → Status: loaded, Origin: config, Version matches Recorded version
   docker exec openclaw openclaw plugins doctor
   # → emits "[plugins] [<name>] register" banner

---

docker restart openclaw

---

docker logs openclaw --since 90s 2>&1 | grep -E "gateway|<name>|plugin"

---

docker exec openclaw openclaw cron run <cron-id>

---

docker exec openclaw stat /home/node/.openclaw/security/audit.jsonl

---

### Evidence 1Boot log compared to a reference working boot

**Reference working boot (v5.3-1, captured 2026-05-04 09:03 CT, before the regression surfaced):**


[gateway] starting...
Config overwrite: /home/node/.openclaw/openclaw.json (sha256 6ad5836a... -> 51a5f87d..., backup=/home/node/.openclaw/openclaw.json.bak)
[gateway] auto-enabled plugins:
  - memory-core tool configured, enabled automatically.
[gateway] starting HTTP server...
[gateway] http server listening (1 plugin: memory-core; 5.2s)


(Note: even the working boot only listed `memory-core` in the `auto-enabled plugins:` block. The extension hooks were nevertheless wired during this boot — `audit.jsonl` advanced normally. So the visible log line is not a complete view of what the loader does, which makes silent failures hard to detect.)

**Broken boot (every restart since 2026-05-04 ~09:02 CT, multiple restarts attempted today including a fresh v5.4 promote):**


2026-05-05T19:53:35.661 [gateway] http server listening (1 plugin: memory-core; 1.2s)


The `Config overwrite` line is also missing on broken boots, even when `openclaw.json`'s sha256 visibly changed via `openclaw config patch`. Whatever code path detects the config delta and triggers extension load is not firing.

### Evidence 2Config state (every key verified)


$ docker exec openclaw openclaw config get plugins.allow
[
  "anthropic",
  "approval-formatter",
  "claw-guard",
  "google",
  "memory-core",
  "ollama",
  "openai",
  "telegram"
]

$ docker exec openclaw openclaw config get plugins.load.paths
[
  "/home/node/.openclaw/extensions/approval-formatter",
  "/home/node/.openclaw/extensions/claw-guard"
]

$ docker exec openclaw openclaw config get plugins.entries.claw-guard
{ "enabled": true }

$ docker exec openclaw openclaw config get plugins.entries.approval-formatter
{ "enabled": true }

$ docker exec openclaw openclaw config get plugins.bundledDiscovery
"compat"


### Evidence 3CLI process loads the plugin fine (proves it is not a code/packaging bug)


$ docker exec openclaw openclaw plugins inspect claw-guard
[plugins] [claw-guard] ClawGuard v3.1.1 active — blockOnThreat=true, logLevel=normal, auditLog=/home/node/.openclaw/security/audit.jsonl
[plugins] [claw-guard] ClawGuard selftest passed — 9 detectors healthy
[plugins] [claw-guard] ClawGuard v3.1.1 registered: 2 hooks, 5 commands

ClawGuard
id: claw-guard
Status: loaded
Origin: config           ← matches approval-formatter (after `doctor --fix` migrated from `global`)
Version: 3.1.1
Recorded version: 3.1.1
Source path: ~/.openclaw/extensions/claw-guard
Install path: ~/.openclaw/extensions/claw-guard



$ docker exec openclaw openclaw plugins doctor
[plugins] [approval-formatter] Approval Formatter v1.0.0 loaded
[plugins] [approval-formatter] Registered message_sending hook
[plugins] [claw-guard] ClawGuard v3.1.1 active — ...
[plugins] [claw-guard] ClawGuard selftest passed — 9 detectors healthy
[plugins] [claw-guard] ClawGuard v3.1.1 registered: 2 hooks, 5 commands


Every CLI invocation prints the register banner. Selftest passes. Source tree intact. `node --check index.js` exits 0.

### Evidence 4Filesystem and permissions


$ docker exec openclaw ls -la /home/node/.openclaw/extensions/claw-guard/
drwxr-xr-x  3 node node  4096 May  5 19:53 .
drwxr-xr-x  4 node node  4096 May  5 19:53 ..
-rw-r--r--  1 node node 42379 Apr 15 13:14 index.js
-rw-r--r--  1 node node  1629 Apr 15 13:14 openclaw.plugin.json
-rw-r--r--  1 node node   506 Apr 15 13:14 package.json


Permissions are owned by `node:node` (uid 1000), readable by the container runtime user.

### Evidence 5The hook never fires under load


$ docker exec openclaw stat /home/node/.openclaw/security/audit.jsonl
  File: /home/node/.openclaw/security/audit.jsonl
  Size: 1111357     Blocks: 2176       IO Block: 4096   regular file
Modify: 2026-05-04 09:02:36.000000000 -0500
Change: 2026-05-04 09:02:36.000000000 -0500

# No writes for ~36+ hours despite 50+ cron runs in that window


Triggered 5+ different crons today via `openclaw cron run <id>` — all returned `status: ok` with normal token usage. Audit `Modify` timestamp never advanced. Hook is unwired in the gateway.

### Evidence 6Hot-reload events visible in logs but not effective

After running `openclaw plugins disable claw-guard && openclaw plugins enable claw-guard`:


2026-05-05T19:15:55 [reload] config change detected; evaluating reload (meta.lastTouchedAt, plugins.entries.claw-guard.enabled)
2026-05-05T19:15:55 [reload] config hot reload applied (plugins.entries.claw-guard.enabled)


Hot reload "applied" — but follow-up cron run still produced no audit growth. The hot-reload event seems to update the config snapshot without actually re-binding extension hooks into the dispatch path.

### Evidence 7Workarounds tried (none effective)

| # | Action | Outcome |
|---|---|---|
| 1 | `docker restart openclaw` (multiple times) | Boots clean, only `memory-core` loaded |
| 2 | `openclaw plugins disable claw-guard && enable claw-guard` | `[reload] config hot reload applied` logged, hook still unwired |
| 3 | `openclaw plugins registry --refresh` | Persisted registry rebuilt; no behavior change |
| 4 | Promote `v5.3-1 → v5.4` via `docker compose build --build-arg OPENCLAW_VERSION=2026.5.4` | New version running, same loader behavior |
| 5 | `openclaw plugins install --dangerously-force-unsafe-install /tmp/claw-guard.tar.gz` | Fixed `recorded version: 1.0.0 → 3.1.1` mismatch. Loader still skips. |
| 6 | `openclaw config patch` to restore `plugins.load.paths` after uninstall stripped it | Path restored. Loader still skips. |
| 7 | `openclaw doctor --fix` | Migrated `Origin: global → Origin: config` (matching the working `approval-formatter`). Loader still skips. |
| 8 | `openclaw config patch --set plugins.bundledDiscovery=compat` (per v5.4 release notes) | Key set. Loader still skips. |
RAW_BUFFERClick to expand / collapse

Bug type

Regression (worked before, now fails)

Beta release blocker

No

Summary

Starting with v2026.5.3, the long-running gateway daemon silently fails to load extension plugins from plugins.load.paths, even when every config key checks out. Bundled plugins (memory-core) load normally; extensions under /home/node/.openclaw/extensions/<name>/ are skipped with no error, no warning, no log line. The same plugin loads cleanly inside short-lived CLI processes (openclaw plugins inspect, openclaw plugins doctor) — those processes emit the plugin's register banner every time. So the failure is gateway-daemon-specific, not a packaging, manifest, or syntax problem.

Confirmed on two unrelated extensions in the same install:

  • claw-guard v3.1.1 (security audit hook — registers before_tool_call)
  • approval-formatter v1.0.0 (Discord/Telegram approval message formatter)

Both v2026.5.3-1 and v2026.5.4 are affected. Promote from v5.3-1 → v5.4 did not resolve. None of the v5.4 release-note candidates (bundledDiscovery: "compat", plugins.entries.<name>.origin: "config", doctor --fix, plugin reinstall, registry refresh) restored extension loading.

The user-visible impact is silent at the operational layer: tool calls still execute, crons return status: ok. The security layer (ClawGuard before_tool_call hook) never fires, so audit.jsonl freezes and tool-call scanning is offline. We only caught it because a separate cron monitors audit.jsonl mtime and alerted to Discord every 30 minutes after stagnation crossed threshold.

Steps to reproduce

Minimal repro on a single-host Docker install:

  1. Install a custom extension at /home/node/.openclaw/extensions/<name>/ with valid openclaw.plugin.json, index.js, package.json. Source must export register and activate. (We have two such extensions; both fail identically.)

  2. Verify config has all the right keys:

    docker exec openclaw openclaw config get plugins.allow            # must include <name>
    docker exec openclaw openclaw config get plugins.load.paths       # must include extension dir
    docker exec openclaw openclaw config get plugins.entries.<name>   # { "enabled": true }
  3. Verify the plugin is healthy in a CLI process:

    docker exec openclaw openclaw plugins inspect <name>
    # → Status: loaded, Origin: config, Version matches Recorded version
    docker exec openclaw openclaw plugins doctor
    # → emits "[plugins] [<name>] register" banner
  4. Restart the container:

    docker restart openclaw
  5. Check gateway boot logs:

    docker logs openclaw --since 90s 2>&1 | grep -E "gateway|<name>|plugin"
    • Expected: an [<name>] register banner from the gateway boot, plus the gateway listening line should reference the extension count.
    • Actual: only [gateway] http server listening (1 plugin: memory-core; X.Xs). Zero output for the extension. No error. No warning. The structured log at /tmp/openclaw/openclaw-YYYY-MM-DD.log also contains nothing about the extension load attempt during the boot window.
  6. Trigger any cron whose prompt invokes a tool:

    docker exec openclaw openclaw cron run <cron-id>

    Returns status: ok with normal token usage. Tools run.

  7. Check the extension's side-effect (in our case, audit log mtime):

    docker exec openclaw stat /home/node/.openclaw/security/audit.jsonl

    Modify timestamp does not advance. Hook never fired. No audit entry written.

Expected behavior

At gateway boot, every entry in plugins.load.paths should be evaluated. Each one that is plugins.allow-listed and plugins.entries.<name>.enabled = true should be loaded into the long-running daemon, its register() hook invoked, and a load/register/error line written to either docker logs openclaw or /tmp/openclaw/openclaw-*.log. Subsequent agent tool calls inside cron runs should fire the registered hooks (before_tool_call, etc.) so plugins like claw-guard can append to their audit log.

If a plugin path is rejected (signature, version mismatch, schema, capability), the gateway should emit a clear error or warning naming the path and the reason — currently it emits nothing at all.

Actual behavior

Gateway boot loads only the bundled memory-core plugin. Extension paths are silently skipped — no register banner, no error, no warning. The CLI-side view (openclaw plugins inspect, openclaw plugins doctor) keeps reporting Status: loaded and prints the register banner because each CLI invocation re-imports the plugin in its own short-lived Node process, which works fine. The long-running gateway daemon, which is the process that needs the hook wired to handle tool calls, never registers the extension.

Net effect: cron runs return status: ok, the agent makes tool calls, and the before_tool_call hook the extension registered is never invoked. Audit log freezes. Approval formatter never reformats messages. The user has no signal from the gateway logs that anything was wrong.

OpenClaw version

v2026.5.4 (current — confirmed by docker exec openclaw openclaw --version) Also reproduces on v2026.5.3-1 (the previous version, before tonight's promote). Last known-good was v2026.5.2 (audit log was advancing prior to the v5.3 promote).

Operating system

  • Container OS: Debian (default OpenClaw image base) - Container runtime: Node.js v24.14.0 inside the container - Host OS: Raspberry Pi OS Bookworm (kernel 6.x), ARM64 (aarch64) - Container engine: Docker 27.x, Docker Compose v2

Install method

Docker Compose with a custom-built image. The image is built from the official ghcr.io/openclaw/openclaw base via: bash docker compose build --build-arg OPENCLAW_VERSION=2026.5.4 openclaw docker compose up -d openclaw Plugin install paths: - memory-core — bundled with the image at /app/dist/extensions/memory-core/ (loads correctly). - claw-guard — originally installed at v1.0.0 via openclaw plugins install --link /home/node/.openclaw/extensions/claw-guard, then in-place updated through minor versions to v3.1.1 over several months. Today reinstalled cleanly via openclaw plugins install --dangerously-force-unsafe-install /tmp/claw-guard.tar.gz to fix a stale recorded version: 1.0.0 mismatch — recorded version is now 3.1.1 matching the manifest. Loader still skips. - approval-formatter — installed at v1.0.0 via openclaw plugins install --link /home/node/.openclaw/extensions/approval-formatter. Manifest version matches recorded version (1.0.0). Loader still skips.

Model

openai-codex/gpt-5.4

Provider / routing chain

openai-codex provider, authenticated via OpenAI Codex subscription using openclaw models auth login --provider openai-codex (saved auth profile, no API key). This is the model the affected cron uses. The bug reproduces regardless of model — we observed the same loader skip when crons routed to other providers earlier in the v5.3 timeline.

Additional provider/model setup details

Not relevant to this bug. The gateway never reaches the model/provider step — extensions fail to load before any tool call dispatch. Same failure occurs with any model selected.

Logs, screenshots, and evidence

### Evidence 1 — Boot log compared to a reference working boot

**Reference working boot (v5.3-1, captured 2026-05-04 09:03 CT, before the regression surfaced):**


[gateway] starting...
Config overwrite: /home/node/.openclaw/openclaw.json (sha256 6ad5836a... -> 51a5f87d..., backup=/home/node/.openclaw/openclaw.json.bak)
[gateway] auto-enabled plugins:
  - memory-core tool configured, enabled automatically.
[gateway] starting HTTP server...
[gateway] http server listening (1 plugin: memory-core; 5.2s)


(Note: even the working boot only listed `memory-core` in the `auto-enabled plugins:` block. The extension hooks were nevertheless wired during this boot — `audit.jsonl` advanced normally. So the visible log line is not a complete view of what the loader does, which makes silent failures hard to detect.)

**Broken boot (every restart since 2026-05-04 ~09:02 CT, multiple restarts attempted today including a fresh v5.4 promote):**


2026-05-05T19:53:35.661 [gateway] http server listening (1 plugin: memory-core; 1.2s)


The `Config overwrite` line is also missing on broken boots, even when `openclaw.json`'s sha256 visibly changed via `openclaw config patch`. Whatever code path detects the config delta and triggers extension load is not firing.

### Evidence 2 — Config state (every key verified)


$ docker exec openclaw openclaw config get plugins.allow
[
  "anthropic",
  "approval-formatter",
  "claw-guard",
  "google",
  "memory-core",
  "ollama",
  "openai",
  "telegram"
]

$ docker exec openclaw openclaw config get plugins.load.paths
[
  "/home/node/.openclaw/extensions/approval-formatter",
  "/home/node/.openclaw/extensions/claw-guard"
]

$ docker exec openclaw openclaw config get plugins.entries.claw-guard
{ "enabled": true }

$ docker exec openclaw openclaw config get plugins.entries.approval-formatter
{ "enabled": true }

$ docker exec openclaw openclaw config get plugins.bundledDiscovery
"compat"


### Evidence 3 — CLI process loads the plugin fine (proves it is not a code/packaging bug)


$ docker exec openclaw openclaw plugins inspect claw-guard
[plugins] [claw-guard] ClawGuard v3.1.1 active — blockOnThreat=true, logLevel=normal, auditLog=/home/node/.openclaw/security/audit.jsonl
[plugins] [claw-guard] ClawGuard selftest passed — 9 detectors healthy
[plugins] [claw-guard] ClawGuard v3.1.1 registered: 2 hooks, 5 commands

ClawGuard
id: claw-guard
Status: loaded
Origin: config           ← matches approval-formatter (after `doctor --fix` migrated from `global`)
Version: 3.1.1
Recorded version: 3.1.1
Source path: ~/.openclaw/extensions/claw-guard
Install path: ~/.openclaw/extensions/claw-guard



$ docker exec openclaw openclaw plugins doctor
[plugins] [approval-formatter] Approval Formatter v1.0.0 loaded
[plugins] [approval-formatter] Registered message_sending hook
[plugins] [claw-guard] ClawGuard v3.1.1 active — ...
[plugins] [claw-guard] ClawGuard selftest passed — 9 detectors healthy
[plugins] [claw-guard] ClawGuard v3.1.1 registered: 2 hooks, 5 commands


Every CLI invocation prints the register banner. Selftest passes. Source tree intact. `node --check index.js` exits 0.

### Evidence 4 — Filesystem and permissions


$ docker exec openclaw ls -la /home/node/.openclaw/extensions/claw-guard/
drwxr-xr-x  3 node node  4096 May  5 19:53 .
drwxr-xr-x  4 node node  4096 May  5 19:53 ..
-rw-r--r--  1 node node 42379 Apr 15 13:14 index.js
-rw-r--r--  1 node node  1629 Apr 15 13:14 openclaw.plugin.json
-rw-r--r--  1 node node   506 Apr 15 13:14 package.json


Permissions are owned by `node:node` (uid 1000), readable by the container runtime user.

### Evidence 5 — The hook never fires under load


$ docker exec openclaw stat /home/node/.openclaw/security/audit.jsonl
  File: /home/node/.openclaw/security/audit.jsonl
  Size: 1111357     Blocks: 2176       IO Block: 4096   regular file
Modify: 2026-05-04 09:02:36.000000000 -0500
Change: 2026-05-04 09:02:36.000000000 -0500

# No writes for ~36+ hours despite 50+ cron runs in that window


Triggered 5+ different crons today via `openclaw cron run <id>` — all returned `status: ok` with normal token usage. Audit `Modify` timestamp never advanced. Hook is unwired in the gateway.

### Evidence 6 — Hot-reload events visible in logs but not effective

After running `openclaw plugins disable claw-guard && openclaw plugins enable claw-guard`:


2026-05-05T19:15:55 [reload] config change detected; evaluating reload (meta.lastTouchedAt, plugins.entries.claw-guard.enabled)
2026-05-05T19:15:55 [reload] config hot reload applied (plugins.entries.claw-guard.enabled)


Hot reload "applied" — but follow-up cron run still produced no audit growth. The hot-reload event seems to update the config snapshot without actually re-binding extension hooks into the dispatch path.

### Evidence 7 — Workarounds tried (none effective)

| # | Action | Outcome |
|---|---|---|
| 1 | `docker restart openclaw` (multiple times) | Boots clean, only `memory-core` loaded |
| 2 | `openclaw plugins disable claw-guard && enable claw-guard` | `[reload] config hot reload applied` logged, hook still unwired |
| 3 | `openclaw plugins registry --refresh` | Persisted registry rebuilt; no behavior change |
| 4 | Promote `v5.3-1 → v5.4` via `docker compose build --build-arg OPENCLAW_VERSION=2026.5.4` | New version running, same loader behavior |
| 5 | `openclaw plugins install --dangerously-force-unsafe-install /tmp/claw-guard.tar.gz` | Fixed `recorded version: 1.0.0 → 3.1.1` mismatch. Loader still skips. |
| 6 | `openclaw config patch` to restore `plugins.load.paths` after uninstall stripped it | Path restored. Loader still skips. |
| 7 | `openclaw doctor --fix` | Migrated `Origin: global → Origin: config` (matching the working `approval-formatter`). Loader still skips. |
| 8 | `openclaw config patch --set plugins.bundledDiscovery=compat` (per v5.4 release notes) | Key set. Loader still skips. |

Impact and severity

Operational impact: None. Tool calls execute. Crons run. Agent reasoning works. No outage at the user-facing layer.

Security impact: Significant. ClawGuard's before_tool_call hook is silently inactive in the gateway, so:

  • No prompt-injection scanning on agent tool calls
  • No credential-leak detection
  • No dangerous-command pattern matching
  • No exfiltration-attempt blocking
  • No persistent audit trail of tool calls (the durable record we rely on for incident review)
  • Approval-formatter not running either, so any approval prompts route through Discord with raw exec text instead of the human-readable formatting

Detection difficulty: High without out-of-band monitoring. Nothing in docker logs openclaw indicates the failure. The (1 plugin: memory-core) listening line looks identical between working and broken boots. We discovered this only because we wrote a separate cron to monitor audit.jsonl mtime and alert on stagnation.

Severity: We'd call this High for any operator who relies on extension hooks for security or compliance. Medium-Low for operators who treat extensions as nice-to-have UX features.

Additional information

Migration history of the affected install

claw-guard was originally installed at v1.0.0 via openclaw plugins install --link <path>. Over the following months, the source was updated in-place to v3.1.1 by editing files at the linked path directly (no openclaw plugins install --force re-run). This left installs.json with recorded version: 1.0.0 while the manifest reported version: 3.1.1. Tonight we reconciled this via a tarball reinstall (install --dangerously-force-unsafe-install /tmp/claw-guard.tar.gz) — recorded version is now 3.1.1 and matches the manifest. The loader still skips, so the version mismatch was not the cause, but it may be worth a separate doctor check that flags or auto-repairs recorded version != manifest version for --link-installed plugins.

Asks for the maintainer team

  1. Confirm whether this is a known v5.3+ regression — or, if there is a config key required for v5.3+ that we are missing, document it explicitly. The current v5.3 / v5.4 release notes describe bundledDiscovery, the new plugins.entries[].origin field, and several plugin loader fixes, but none describe the symptom we're seeing.

  2. Add a startup log line for every extension path considered — at minimum, list which paths were attempted, which loaded, and which were rejected (with reason). Today the gateway emits only a bundled-plugin count, so operators have no signal that their extensions were even seen.

  3. Add a doctor check that diffs gateway-loaded plugins against config-declared extensions — would catch this class of bug at install/promote time rather than relying on each operator to hand-roll an audit-log monitor.

  4. Consider a fail-LOUD default for security-critical extensionsclaw-guard declares itself a security plugin in its manifest; silently dropping a security plugin is the opposite of safe-by-default.

Reproduction artifacts available on request

  • Container's openclaw.json (sanitized — secrets stripped)
  • installs.json snapshot before and after the tarball reinstall
  • Full /tmp/openclaw/openclaw-2026-05-05.log boot windows
  • The custom audit-stagnation-check.sh monitor that surfaces this failure mode

Happy to provide any of the above if it would help triage. Also happy to test patches against this install — the affected gateway is on a Tailscale-reachable Pi and we can stand up a v5.x staging container alongside production for any candidate fix.

extent analysis

TL;DR

The most likely fix or workaround for the issue is to modify the plugins.load.paths configuration to ensure that the extension paths are correctly loaded by the gateway daemon.

Guidance

  1. Verify configuration keys: Double-check that plugins.allow, plugins.load.paths, and plugins.entries.<name> are correctly set in the openclaw.json configuration file.
  2. Check extension paths: Ensure that the extension paths in plugins.load.paths are correct and match the actual locations of the extensions.
  3. Run openclaw plugins doctor: Execute the openclaw plugins doctor command to detect and potentially fix any issues with the plugin installations.
  4. Monitor gateway logs: Closely monitor the gateway logs for any errors or warnings related to plugin loading to identify potential issues.

Example

No specific code snippet is provided as the issue seems to be related to configuration and plugin loading rather than a code-level problem.

Notes

The issue appears to be specific to the v2026.5.3 and v2026.5.4 versions of OpenClaw, and the root cause is not immediately clear. Further investigation and potentially more information from the maintainers may be necessary to fully resolve the issue.

Recommendation

Apply a workaround by modifying the plugins.load.paths configuration and closely monitoring the gateway logs for any errors or warnings related to plugin loading. If the issue persists, consider reaching out to the maintainers for further assistance or waiting for a potential fix in a future version.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

At gateway boot, every entry in plugins.load.paths should be evaluated. Each one that is plugins.allow-listed and plugins.entries.<name>.enabled = true should be loaded into the long-running daemon, its register() hook invoked, and a load/register/error line written to either docker logs openclaw or /tmp/openclaw/openclaw-*.log. Subsequent agent tool calls inside cron runs should fire the registered hooks (before_tool_call, etc.) so plugins like claw-guard can append to their audit log.

If a plugin path is rejected (signature, version mismatch, schema, capability), the gateway should emit a clear error or warning naming the path and the reason — currently it emits nothing at all.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING