I'm developing a plugin that uses hooks so I can log LLM calls Openclaw makes. The plugin is starting simple but will evolve to something more complex. It seems, however, that new versions of the plugin don't always get loaded when I do a gateway restart or stop/start sequence. How can I ensure that restarts, or stop/start sequences, always load the latest version of the plugin?
#Ensuring a custom Openclaw plugin loads at gateway start / restart?
1 messages · Page 1 of 1 (latest)
For local plugin development, the least surprising path is to install it as a linked plugin, not as a copied install:
openclaw plugins install --link /absolute/path/to/your-plugin
openclaw plugins enable <plugin-id>
openclaw plugins list --enabled --verbose
openclaw gateway restart
A normal openclaw plugins install /path/to/plugin copies the plugin into OpenClaw’s managed extensions dir, so editing your source tree later will not necessarily change what the gateway loads. --link adds the source dir to plugins.load.paths, so restarts discover that directory directly.
A few gotchas:
- If your manifest/package points at
dist/index.js, rebuild before restarting. The gateway will load the built file, not your editedsrc/index.ts. - If you are testing an installed/copy-based plugin instead of a linked one, reinstall/update it before restart:
openclaw plugins install /path/to/plugin --forceoropenclaw plugins update <id>. - Verify what OpenClaw thinks it loaded with
openclaw plugins inspect <plugin-id> --jsonoropenclaw plugins list --verbose; check the shown source/path/version.
Docs: https://docs.openclaw.ai/cli/plugins and https://docs.openclaw.ai/plugins/building-plugins
If it still loads an old copy after that, please share openclaw --version, whether you used --link, and the source path from openclaw plugins inspect <plugin-id> --json.
Thanks, that's helpful.
I tried
openclaw plugins install --link
and it told me the plugin was already installed, which makes sense since it's already in plugins.load.paths.
However, when I run
openclaw plugins list --enabled --verbose
the plugin doesn't appear in the list even though it's in plugins.load.paths, plugins.allow, and plugins.entries with enabled: true. I can verify the plugin works correctly — when I issue a gateway config.get tool call from within the session, it triggers a config re-read and the plugin activates and starts logging perfectly.
What could cause a workspace plugin (kind: workspace) to be silently skipped during auto-enable on boot when it loads fine after a config re-read? The plugin only uses hooks (model_call_started/model_call_ended) and doesn't need any plugin slot — is that relevant?
Follow-ups I'm wondering about:
- Does a plugin need to be referenced by a plugins.slots entry to be guaranteed to load on every boot?
- If so, is there an alternative way to ensure a hooks-only workspace plugin loads reliably on gateway start/restart?
- Does openclaw plugins inspect <id> --json work for plugins in plugins.load.paths, or does it only inspect plugins installed via openclaw plugins install?
I checked the current plugin startup path. Short version: no, a hooks-only plugin should not need a plugins.slots entry.
Answers:
plugins.slotsis only for exclusive plugin kinds likememory/context-engine. It is not the general “load this plugin” mechanism.- For a hooks-only local plugin, the intended reliable boot path is: discoverable via
plugins.load.paths, allowed if you useplugins.allow, and explicitly enabled viaplugins.entries.<id>.enabled: true. Current source has a startup planner path for explicitly enabled non-channel “sidecar” plugins, which is exactly the bucket a hooks-only plugin should fall into. openclaw plugins inspect <id> --jsonshould work for discoverable plugins fromplugins.load.paths; it is not limited to managed installs. For linked/path plugins theinstallfield may be null, but the plugin should still inspect.
The part that smells off is “loads after config.get”. config.get builds the runtime config schema with plugins, so it can cause plugin discovery/loading through a different path than gateway startup. If startup skips it but schema/config reload sees it, I’d check these first:
openclaw --version
openclaw plugins inspect <plugin-id> --json
openclaw plugins list --json
In the JSON, look for the plugin’s exact canonical id, origin, status, source, and any diagnostics. Also make sure the id in plugins.allow and plugins.entries.<id> exactly matches the manifest id.
One nuance: if you literally put kind: "workspace" in openclaw.plugin.json, I’d remove that. “workspace” is an origin, not a documented manifest kind; documented kind values are slot kinds like memory / context-engine. A normal hooks-only plugin can omit kind entirely.
For clarity I’d configure it like this:
plugins: {
enabled: true,
allow: ["your-plugin-id"],
load: {
paths: ["/absolute/path/to/your-plugin"],
},
entries: {
"your-plugin-id": {
enabled: true,
},
},
}
If that still skips on cold boot on your version, that sounds like a startup planner bug, not a slots requirement. Please include openclaw --version and the inspect --json / list --json diagnostics if you file it: https://github.com/openclaw/openclaw/issues
Thanks for the excellent insights and tips.
I tried the suggestions, but I'm still hitting the same problem. Here's what I did and what I found (note: the plugin is called "llm-monitor"):
Changes made:
- Removed
"kind": "workspace"from the manifest (as advised — it's not a valid manifest kind) - Verified the plugin is already in
plugins.load.paths,plugins.allow, andplugins.entries.<id>.enabled: true
What happened after a cold stop/start:
- Gateway started with
http server listening (2 plugins: browser, ctx-engine; 12.5s)— llm-monitor was not loaded - No
[plugins] [llm-monitor]message in the journal at all - The CSV log file has no entries from the new session
What openclaw plugins list --json shows:
- llm-monitor is listed with
origin: "config",enabled: true,status: "loaded" - But the diagnostics section at the top warns:
"Persisted plugin registry policy does not match current config; using derived plugin index. Run openclaw plugins registry --refresh to update the persisted registry."
This suggests a stale persisted registry might be the root cause. The config-derived index knows about the plugin (it shows in list --json), but the startup planner might be loading from the stale registry instead.
Also notable: openclaw plugins inspect <id> --json and openclaw plugins list --json hang when run from the CLI while the gateway is running — they seem to wait for a gateway response that times out. I got the output through the tool's exec instead.
Questions:
- Could the stale persisted registry explain why the plugin isn't loaded on cold boot despite being in
plugins.load.paths+plugins.allow+plugins.entries? - Is
openclaw plugins registry --refreshsafe to run while the gateway is running, or does it require a stopped gateway? - If a stale registry is the issue, is there a way to trigger a full config-aware boot that disregards the persisted registry?
Running Openclaw v2026.5.12 .
That warning is relevant, but I think there are two separate things in play on v2026.5.12:
-
v2026.5.12 uses a cold persisted plugin registry for startup planning.
plugins list --jsoncan fall back to a derived index and show the plugin correctly while also warning that the persisted registry is stale. So yes, refresh it. -
For startup loading,
plugins.entries.llm-monitor.enabled: trueis not always enough for a hooks-only plugin anymore. The manifest should declare startup intent. The current manifest docs say: “Omittingactivation.onStartupno longer startup-loads the plugin implicitly.”
For a plugin that must observe all model calls after gateway boot, I’d set this in openclaw.plugin.json:
{
"id": "llm-monitor",
"name": "LLM Monitor",
"configSchema": {
"type": "object",
"additionalProperties": false
},
"activation": {
"onStartup": true,
"onCapabilities": ["hook"]
}
}
Then rebuild if your manifest/runtime is copied into dist, refresh the registry, and restart:
openclaw plugins registry --refresh
openclaw plugins registry --json
openclaw gateway restart
openclaw plugins registry --refresh is intended as the repair path and writes the generated registry state. It should be safe to run while the gateway is up, but it will not mutate the already-running gateway’s active plugin registry; restart afterward. If your CLI hangs while the gateway is running, run the refresh with the gateway stopped, then start it again. The hang itself is worth filing if reproducible, because plugins list / registry inspection is supposed to be local registry/config work, not a long gateway RPC dependency.
For #3: the break-glass env is OPENCLAW_DISABLE_PERSISTED_PLUGIN_REGISTRY=1, but the code marks it deprecated and points people back to openclaw plugins registry --refresh / openclaw doctor --fix. I’d only use that briefly to confirm the diagnosis, not as the fix.
Also: model_call_started / model_call_ended are the right hooks for telemetry without raw prompt/response content. allowConversationAccess=true is only needed for raw conversation hooks like llm_input / llm_output.
Docs: https://docs.openclaw.ai/plugins/manifest and https://docs.openclaw.ai/cli/plugins
Follow-up — resolved.
The fixes worked. Summary of what it took:
-
Added
activation.onStartup: trueandactivation.onCapabilities: ["hook"]to the manifest. That was the main fix — without it, the startup planner wasn't pulling in the hooks-only plugin on cold boot. -
Ran
openclaw plugins registry --refreshto fix the stale persisted registry. Without this, even the corrected manifest was ignored because the startup planner was using cached metadata. -
After those two changes, a cold stop/start brought up the plugin cleanly:
http server listening (XX plugins: ... llm-monitor). The plugin is fully functional.
Also verified that code changes to the entrypoint (index.js) are picked up on restart without any extra steps — only manifest changes require the registry refresh.
Thanks for the help — I wouldn't have found the activation.onStartup: true requirement in the docs on my own.
Nice, glad we pinned it down.
That summary is the right takeaway for future folks:
- hooks-only plugin that must be active from boot:
activation.onStartup: true - hook capability hint:
activation.onCapabilities: ["hook"] - after manifest/policy changes:
openclaw plugins registry --refresh - after runtime/entrypoint code changes: gateway restart is enough for linked/path plugins
If this fixed the thread, please right-click the solution message → Apps → ✅ Mark Solution so Answer Overflow indexes it cleanly.