openclaw - ✅(Solved) Fix Plugin loader rejects node-owned plugin dirs with misleading "stale config entry" surrounding warnings; container restart auto-reverts manual chown [1 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#76144Fetched 2026-05-03 04:41:52
View on GitHub
Comments
1
Participants
2
Timeline
8
Reactions
2
Timeline (top)
referenced ×5commented ×1cross-referenced ×1unsubscribed ×1

Error Message

  1. Detection-only change to error text. When the loader has already emitted a blocked plugin candidate: suspicious ownership for a path, suppress or rephrase the downstream "plugin not found / remove it from plugins config" warning for the same plugin id. Suggested replacement: plugin <id> present but blocked; see preceding ownership warning. Run \chown root:root <path>` to allow load.`

Root Cause

Manual chown -R 0:0 /data/plugins/<plugin> does fix the issue, but a docker restart <container> reverts the ownership back to node:node because the container entrypoint chowns /data recursively on boot. So the fix is non-durable in any plugin-developer workflow that involves restarting the container.

Fix Action

Fixed

PR fix notes

PR #76151: fix(plugins): replace misleading stale-config warnings for ownership-blocked plugins and deduplicate suspicious-ownership diagnostics

Description (problem / solution / changelog)

Problem

When a plugin directory is blocked by file ownership (uid mismatch), the gateway emits two categories of compounding bad UX:

Defect A — misleading "stale config entry" warnings: for every reference to the blocked plugin in config (entries, allow, deny, slots), the gateway emits plugin not found: <id> (stale config entry ignored; remove it from plugins config). This actively misdirects operators into deleting valid config blocks.

Defect B — duplicate "suspicious ownership" warnings: the same diagnostic fires once per discovery scope (scoped result + shared result) rather than once per blocked path per boot, producing 2–3 identical lines.

Addresses #76144.

Root cause

isUnsafePluginCandidate (discovery.ts) pushed a PluginDiagnostic without a pluginId field. Without a pluginId, pushRegistryDiagnostics (validation.ts) could not tell that "plugin not found for id X" corresponded to a blocked-ownership candidate — so pushMissingPluginIssue emitted the stale-config message without checking for block signals.

For Defect B: discoverOpenClawPlugins runs two separate discovery passes (scopedResult, sharedResult) each with their own seen set. The global extensions dir (roots.global) is scanned in sharedResult; if the same path also appears in extraPaths (in scopedResult), the cross-scope merge in mergeDiscoveryResult appended diagnostics unconditionally.

Changes

src/plugins/discovery.ts

  • isUnsafePluginCandidate: add idHint?: string param; set pluginId: params.idHint on the pushed diagnostic; inline-dedup prevents same-array duplicates via pluginId+message key
  • addCandidate: pass idHint through to isUnsafePluginCandidate; add the resolved path to seen even when blocked, so a second addCandidate call for the same path returns immediately without re-evaluating
  • mergeDiscoveryResult: dedup diagnostics by pluginId+message key when merging scopedResult and sharedResult into the final result

src/config/validation.ts

  • Before pushMissingPluginIssue: build blockedOwnershipPluginIds from registry diagnostics that have pluginId and contain "suspicious ownership"
  • Inside pushMissingPluginIssue: if pluginId is in blockedOwnershipPluginIds, emit "plugin <id> present but blocked by file ownership; run \chown root:root <plugin-path>` to allow load"` instead of the stale-config message

src/plugins/discovery.test.ts

  • New test: attaches idHint as pluginId on blocked-ownership diagnostics — asserts diagnostic.pluginId === "my-plugin" when ownershipUid mismatches
  • New test: emits blocked-ownership diagnostic only once per path even when seen via multiple discovery paths — passes the same path twice via extraPaths (triggering both the scoped and shared discovery of the same dir), asserts ownershipDiags.length === 1

Test result

53 passed (53) in src/plugins/discovery.test.ts. oxlint: 0 warnings, 0 errors on all changed files.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/config/validation.ts (modified, +16/-0)
  • src/plugins/discovery.test.ts (modified, +55/-0)
  • src/plugins/discovery.ts (modified, +23/-6)

Code Example

[gateway] [plugins] blocked plugin candidate: suspicious ownership 
  (/data/plugins/<plugin-name>, uid=1000, expected uid=0 or root)

---

- plugins.entries.<id>: plugin not found: <id> (stale config entry ignored; remove it from plugins config)
- plugins.allow: plugin not found: <id> (stale config entry ignored; remove it from plugins config)

---

# State: openclaw 2026.4.12 in a container; plugin dir bind-mounted from host.
$ docker exec openclaw-1 ls -ld /data/plugins/my-plugin
drwxr-xr-x 7 node node ... /data/plugins/my-plugin

$ docker exec -d openclaw-1 sh -c 'nohup openclaw gateway > gw.log 2>&1 &'
$ docker exec openclaw-1 grep "blocked plugin" gw.log
[gateway] [plugins] blocked plugin candidate: suspicious ownership 
  (/data/plugins/my-plugin/observability-plugin, uid=1000, expected uid=0 or root)
[gateway] [plugins] blocked plugin candidate: suspicious ownership 
  (/data/plugins/my-plugin/observability-plugin, uid=1000, expected uid=0 or root)
[gateway] [plugins] blocked plugin candidate: suspicious ownership 
  (/data/plugins/my-plugin/observability-plugin, uid=1000, expected uid=0 or root)
# also: "plugins.entries.my-plugin: plugin not found ... remove it from plugins config"

# Apply manual fix:
$ docker exec openclaw-1 chown -R 0:0 /data/plugins/my-plugin
$ docker exec openclaw-1 ls -ld /data/plugins/my-plugin
drwxr-xr-x 7 root root ...

# Restart container:
$ docker restart openclaw-1
$ docker exec openclaw-1 ls -ld /data/plugins/my-plugin
drwxr-xr-x 7 node node ...   ← reverted
RAW_BUFFERClick to expand / collapse

Problem

On openclaw 2026.4.12, the gateway plugin loader rejects any plugin whose source directory is owned by the node user (uid 1000) with:

[gateway] [plugins] blocked plugin candidate: suspicious ownership 
  (/data/plugins/<plugin-name>, uid=1000, expected uid=0 or root)

This is reasonable defense-in-depth, but it ships with three compounding UX defects that turn a one-line fix into a multi-step debugging exercise:

Defect A — surrounding warnings actively misdirect

Whenever a plugin is blocked, the gateway also emits:

- plugins.entries.<id>: plugin not found: <id> (stale config entry ignored; remove it from plugins config)
- plugins.allow: plugin not found: <id> (stale config entry ignored; remove it from plugins config)

These tell the operator the plugin doesn't exist and to remove the config entry — when in fact the plugin IS present, just blocked on ownership. Following the suggestion makes things worse (the operator deletes a valid config block).

Defect B — duplicated warnings

Each blocked plugin emits the suspicious ownership warning 2–3 times per gateway boot. Sometimes from [gateway] [plugins] and [plugins] both, sometimes from a separate "Config warnings" pre-block. The duplication adds noise to an already noisy startup.

Defect C — chown reverts on container restart

Manual chown -R 0:0 /data/plugins/<plugin> does fix the issue, but a docker restart <container> reverts the ownership back to node:node because the container entrypoint chowns /data recursively on boot. So the fix is non-durable in any plugin-developer workflow that involves restarting the container.

Repro

# State: openclaw 2026.4.12 in a container; plugin dir bind-mounted from host.
$ docker exec openclaw-1 ls -ld /data/plugins/my-plugin
drwxr-xr-x 7 node node ... /data/plugins/my-plugin

$ docker exec -d openclaw-1 sh -c 'nohup openclaw gateway > gw.log 2>&1 &'
$ docker exec openclaw-1 grep "blocked plugin" gw.log
[gateway] [plugins] blocked plugin candidate: suspicious ownership 
  (/data/plugins/my-plugin/observability-plugin, uid=1000, expected uid=0 or root)
[gateway] [plugins] blocked plugin candidate: suspicious ownership 
  (/data/plugins/my-plugin/observability-plugin, uid=1000, expected uid=0 or root)
[gateway] [plugins] blocked plugin candidate: suspicious ownership 
  (/data/plugins/my-plugin/observability-plugin, uid=1000, expected uid=0 or root)
# also: "plugins.entries.my-plugin: plugin not found ... remove it from plugins config"

# Apply manual fix:
$ docker exec openclaw-1 chown -R 0:0 /data/plugins/my-plugin
$ docker exec openclaw-1 ls -ld /data/plugins/my-plugin
drwxr-xr-x 7 root root ...

# Restart container:
$ docker restart openclaw-1
$ docker exec openclaw-1 ls -ld /data/plugins/my-plugin
drwxr-xr-x 7 node node ...   ← reverted

Root cause (suspected)

A version-bump in 2026.4.12 (or close to it) tightened the plugin-loader ownership check, but the supporting warning text was not updated. The "plugin not found" / "stale config entry" warnings were correct in older versions where the only way for a plugin to be missing was a deleted directory — they don't account for the new "blocked but present" state.

Proposed fix

  1. Detection-only change to error text. When the loader has already emitted a blocked plugin candidate: suspicious ownership for a path, suppress or rephrase the downstream "plugin not found / remove it from plugins config" warning for the same plugin id. Suggested replacement: plugin <id> present but blocked; see preceding ownership warning. Run \chown root:root <path>` to allow load.`

  2. Deduplicate the suspicious-ownership warning. Emit it once per (plugin-id, source-path) per gateway boot, not 2–3 times.

  3. Document the constraint. A short doc note (in docs/plugins.md or wherever plugin install lives) explaining that the loader requires root-owned plugin directories, with the chown fix and a note about chown durability across container restarts.

  4. Container-image consideration. For the official openclaw container image, the entrypoint chown should preserve plugin-directory ownership rather than recursively resetting it (or chown to root, not to node). This is a one-line change in entrypoint.sh.

Severity

Confusing-diagnostic flow that costs every plugin developer 30+ minutes the first time. The misleading "remove it from plugins config" advice makes it actively dangerous — operators may follow it and delete valid configuration.

Tested-against

openclaw 2026.4.12, container image based on Ubuntu, plugin dirs bind-mounted from host filesystem.

extent analysis

TL;DR

The most likely fix involves updating the plugin loader's error text and documentation to accurately reflect the new ownership check, as well as modifying the container entrypoint to preserve plugin directory ownership.

Guidance

  • Update the plugin loader's error text to suppress or rephrase the "plugin not found" warning when a plugin is blocked due to suspicious ownership, providing a clear instruction on how to resolve the issue.
  • Deduplicate the "suspicious ownership" warning to emit it only once per plugin ID and source path per gateway boot.
  • Document the requirement for root-owned plugin directories in the plugin installation documentation, including the chown fix and a note about ownership durability across container restarts.
  • Modify the container entrypoint to preserve plugin directory ownership instead of recursively resetting it to the node user.

Example

No code snippet is provided as the issue does not require a specific code change, but rather a modification to the error text and documentation.

Notes

The proposed fix assumes that the issue is specific to the openclaw 2026.4.12 version and may not apply to other versions. Additionally, the fix may require modifications to the container image and entrypoint script.

Recommendation

Apply the proposed fix by updating the error text, documentation, and container entrypoint to resolve the issue and prevent further confusion. This will provide a clear and accurate diagnostic flow for plugin developers and prevent the deletion of valid configuration.

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…

Still need to ship something?

×6

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

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix Plugin loader rejects node-owned plugin dirs with misleading "stale config entry" surrounding warnings; container restart auto-reverts manual chown [1 pull requests, 1 comments, 2 participants]