openclaw - 💡(How to fix) Fix before_install hook not triggered by CLI `openclaw plugins install` [1 pull requests]

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…

The before_install plugin hook is never triggered when installing plugins via the CLI command openclaw plugins install <spec>, even when a plugin that registers a before_install handler is already installed and loaded in the running Gateway. The hook fires correctly when installing via the Gateway chat command /plugins install.

This means enterprise policy and security scanner plugins cannot enforce install-time checks through the most common installation path.

Root Cause

The CLI and Gateway run in separate processes with independent memory spaces. The before_install hook depends on a process-local singleton (getGlobalHookRunner()) that is only initialized when the plugin system is loaded.

CLI process flow:

  1. src/cli/command-catalog.ts — there is no catalog entry for ["plugins", "install"]
  2. src/cli/command-path-policy.ts:12-20 — falls through to default policy loadPlugins: "never"
  3. src/cli/command-bootstrap.ts:33-35ensureCliCommandBootstrap returns immediately when loadPlugins is false, never calling loadOpenClawPlugins()
  4. src/plugins/hook-runner-global.ts:60-62getGlobalHookRunner() returns null in the CLI process
  5. src/plugins/install-security-scan.runtime.ts:726-728 — the null check silently returns undefined, skipping the hook entirely

Gateway process flow (works correctly):

  1. Gateway startup calls loadGatewayStartupPlugins()loadOpenClawPlugins()activatePluginRegistry()initializeGlobalHookRunner()
  2. The hook runner singleton is populated with all registered handlers
  3. runBeforeInstallHook finds the runner and executes the hook

The key issue is that Symbol.for singleton state is process-isolated — the CLI process cannot see the Gateway process's initialized hook runner.

Fix Action

Fixed

Code Example

{
  commandPath: ["plugins", "install"],
  exact: true,
  policy: { loadPlugins: "always" },
},
RAW_BUFFERClick to expand / collapse

Summary

The before_install plugin hook is never triggered when installing plugins via the CLI command openclaw plugins install <spec>, even when a plugin that registers a before_install handler is already installed and loaded in the running Gateway. The hook fires correctly when installing via the Gateway chat command /plugins install.

This means enterprise policy and security scanner plugins cannot enforce install-time checks through the most common installation path.

Steps to Reproduce

  1. Install a plugin that registers a before_install hook handler
  2. Start the Gateway (openclaw gateway run)
  3. Verify the hook is registered (e.g., via probe logging in the handler)
  4. Run: openclaw plugins install @openclaw/browser-plugin
  5. Observe: the before_install handler is never invoked

For comparison, repeating the install via Gateway chat /plugins install @openclaw/browser-plugin correctly triggers the hook.

Expected Behavior

before_install should fire for all plugin installation paths, as documented in PR #56050:

It now covers:

  • interactive skill installs
  • plugin bundle installs
  • plugin package installs
  • single-file plugin installs

CLI openclaw plugins install is a plugin package install path and should be covered.

Root Cause

The CLI and Gateway run in separate processes with independent memory spaces. The before_install hook depends on a process-local singleton (getGlobalHookRunner()) that is only initialized when the plugin system is loaded.

CLI process flow:

  1. src/cli/command-catalog.ts — there is no catalog entry for ["plugins", "install"]
  2. src/cli/command-path-policy.ts:12-20 — falls through to default policy loadPlugins: "never"
  3. src/cli/command-bootstrap.ts:33-35ensureCliCommandBootstrap returns immediately when loadPlugins is false, never calling loadOpenClawPlugins()
  4. src/plugins/hook-runner-global.ts:60-62getGlobalHookRunner() returns null in the CLI process
  5. src/plugins/install-security-scan.runtime.ts:726-728 — the null check silently returns undefined, skipping the hook entirely

Gateway process flow (works correctly):

  1. Gateway startup calls loadGatewayStartupPlugins()loadOpenClawPlugins()activatePluginRegistry()initializeGlobalHookRunner()
  2. The hook runner singleton is populated with all registered handlers
  3. runBeforeInstallHook finds the runner and executes the hook

The key issue is that Symbol.for singleton state is process-isolated — the CLI process cannot see the Gateway process's initialized hook runner.

Impact

  • Security: Enterprise policy plugins cannot block or audit CLI installs. The before_install hook's fail-closed design (failurePolicyByHook: { before_install: "fail-closed" }) is completely bypassed in the CLI path.
  • Documentation mismatch: PR #56050 claims coverage for "plugin package installs" without qualification, but the most common install path (CLI) is not covered.
  • Known since merge: This was flagged as P1 by three review bots (Codex, Greptile, Aisle) before PR #56050 was merged, but the PR was merged without addressing it.

Suggested Fix Directions

Option A — Load plugins for plugins install in CLI:

Add a catalog entry in src/cli/command-catalog.ts:

{
  commandPath: ["plugins", "install"],
  exact: true,
  policy: { loadPlugins: "always" },
},

This would cause the CLI process to load the plugin registry and initialize the hook runner before executing the install. Trade-off: increased CLI startup latency for the install command.

Option B — Delegate CLI installs to Gateway via RPC:

When a Gateway is running, route openclaw plugins install through a Gateway RPC call (e.g., skills.install or a dedicated endpoint) so the install executes in the Gateway process where plugins are already loaded. This avoids the startup cost but requires Gateway connectivity.

References

  • PR #56050 — introduced before_install hook
  • Codex Bot P1 comment on install-security-scan.runtime.ts:42-44: "Initialize hooks before running install security scans"
  • Greptile Bot P1: "block: true is logged but installation is never actually prevented"
  • src/plugins/hook-runner-global.ts — singleton hook runner
  • src/plugins/install-security-scan.runtime.ts:700-771runBeforeInstallHook
  • src/cli/command-catalog.ts — declarative command routing (no ["plugins", "install"] entry)

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