Bug type
Regression (worked before, now fails)
Beta release blocker
No
Summary
Summary
After upgrading from v2026.4.29 to v2026.5.2, agent tools registered by path-based plugins (loaded via plugins.load.paths, manifest origin "config") silently fail to appear
in agent tool palettes. The plugin's register() callback is invoked at gateway startup, but the tool factory is never called when an agent resolves its tool list, so the gateway
returns unknown method errors when the agent tries to call the tool.
This is the same regression family that PR #76536 fixes for capability providers and PR #76393 fixed for memory-plugin doctor/status — the on-demand load fallback that PR #76004
("perf(plugins): reuse startup runtime registry") removed from resolvePluginToolRegistry in src/plugins/tools.ts is also needed for tool resolution.
The Codex review on PR #76004 explicitly flagged this exact risk before merge:
"Keep plugin tools from disappearing on cold registries — resolvePluginTools now only reads the loaded channel registry."
Steps to reproduce
Symptom
Gateway logs:
error Gateway call failed: GatewayClientRequestError: unknown method: <tool-name>.run
info gateway/ws ⇄ res ✗ <tool-name>.run 0ms errorCode=INVALID_REQUEST errorMessage=unknown method: <tool-name>.run
The agent thinks the tool isn't registered and skips/fails the call. No warning, no diagnostic — silent failure.
Minimal reproducer
A 40-line plugin that demonstrates the bug deterministically. Drop into a directory listed under plugins.load.paths, allowlist dummy-test in your config, restart the gateway, and
ask any agent to invoke the tool.
dummy-test/package.json:
{
"name": "@example/dummy-test",
"version": "0.1.0",
"type": "module",
"openclaw": {
"extensions": ["./index.ts"],
"compat": { "pluginApi": ">=2026.3.24-beta.2" }
}
}
dummy-test/openclaw.plugin.json:
{
"id": "dummy-test",
"name": "Dummy Test",
"description": "Minimal plugin to verify path-based tool plugins work.",
"activation": { "onStartup": true },
"contracts": { "tools": ["dummy-test"] },
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {}
}
}
dummy-test/index.ts:
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import type {
AnyAgentTool,
OpenClawPluginApi,
OpenClawPluginToolFactory,
} from "openclaw/plugin-sdk/plugin-entry";
export default definePluginEntry({
id: "dummy-test",
name: "Dummy Test",
description: "Minimal A/B test plugin",
register(api: OpenClawPluginApi) {
api.logger?.info("[dummy-test:DIAG] register() ENTERED");
api.registerTool(
((ctx) => {
api.logger?.info(
`[dummy-test:DIAG] FACTORY called sandboxed=${ctx.sandboxed}`,
);
if (ctx.sandboxed) return null;
const tool: AnyAgentTool = {
name: "dummy-test",
label: "Dummy Test",
description: "Returns 'pong' to verify tool exposure.",
parameters: { type: "object", properties: {}, additionalProperties: false },
async execute() {
return {
content: [{ type: "text" as const, text: "pong" }],
details: { ok: true },
};
},
} as unknown as AnyAgentTool;
return tool;
}) as OpenClawPluginToolFactory,
{ optional: true },
);
api.logger?.info("[dummy-test:DIAG] register() EXITED");
},
});
openclaw.json (relevant snippets):
Expected (works on v2026.4.29)
Gateway log shows both DIAG lines on startup AND on first agent request:
[dummy-test:DIAG] register() ENTERED
[dummy-test:DIAG] register() EXITED
[dummy-test:DIAG] FACTORY called sandboxed=false ← appears at first agent request
Agent invocation of dummy-test returns "pong".
Actual (broken on v2026.5.2)
Gateway log shows only the register() lines on startup. The FACTORY called line never appears, no matter how many agent requests are made:
[dummy-test:DIAG] register() ENTERED
[dummy-test:DIAG] register() EXITED
# ... agent requests come and go, FACTORY never called ...
Agent invocation fails with unknown method: dummy-test.run.
Expected behavior
Expected (works on v2026.4.29)
Gateway log shows both DIAG lines on startup AND on first agent request:
[dummy-test:DIAG] register() ENTERED
[dummy-test:DIAG] register() EXITED
[dummy-test:DIAG] FACTORY called sandboxed=false ← appears at first agent request
Agent invocation of dummy-test returns "pong".
Actual behavior
Summary
After upgrading from v2026.4.29 to v2026.5.2, agent tools registered by path-based plugins (loaded via plugins.load.paths, manifest origin "config") silently fail to appear
in agent tool palettes. The plugin's register() callback is invoked at gateway startup, but the tool factory is never called when an agent resolves its tool list, so the gateway
returns unknown method errors when the agent tries to call the tool.
This is the same regression family that PR #76536 fixes for capability providers and PR #76393 fixed for memory-plugin doctor/status — the on-demand load fallback that PR #76004
("perf(plugins): reuse startup runtime registry") removed from resolvePluginToolRegistry in src/plugins/tools.ts is also needed for tool resolution.
The Codex review on PR #76004 explicitly flagged this exact risk before merge:
"Keep plugin tools from disappearing on cold registries — resolvePluginTools now only reads the loaded channel registry."
This issue is a real-world repro of that warning.
Affected versions
- Broken:
v2026.5.2-beta.2, v2026.5.2
- Working:
v2026.4.29 (rollback fixes the issue immediately)
Environment
- Node.js 22.x
- Linux (Ubuntu) — gateway runs as a systemd service
- Plugin loaded via
plugins.load.paths
- Plugin manifest declares
activation.onStartup: true and contracts.tools: ["<tool-name>"]
- Plugin allowlisted in
openclaw.json (both plugins.allow and tools.allow and agent.tools.allow)
Symptom
Gateway logs:
error Gateway call failed: GatewayClientRequestError: unknown method: <tool-name>.run
info gateway/ws ⇄ res ✗ <tool-name>.run 0ms errorCode=INVALID_REQUEST errorMessage=unknown method: <tool-name>.run
The agent thinks the tool isn't registered and skips/fails the call. No warning, no diagnostic — silent failure.
Minimal reproducer
A 40-line plugin that demonstrates the bug deterministically. Drop into a directory listed under plugins.load.paths, allowlist dummy-test in your config, restart the gateway, and
ask any agent to invoke the tool.
dummy-test/package.json:
{
"name": "@example/dummy-test",
"version": "0.1.0",
"type": "module",
"openclaw": {
"extensions": ["./index.ts"],
"compat": { "pluginApi": ">=2026.3.24-beta.2" }
}
}
dummy-test/openclaw.plugin.json:
{
"id": "dummy-test",
"name": "Dummy Test",
"description": "Minimal plugin to verify path-based tool plugins work.",
"activation": { "onStartup": true },
"contracts": { "tools": ["dummy-test"] },
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {}
}
}
dummy-test/index.ts:
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import type {
AnyAgentTool,
OpenClawPluginApi,
OpenClawPluginToolFactory,
} from "openclaw/plugin-sdk/plugin-entry";
export default definePluginEntry({
id: "dummy-test",
name: "Dummy Test",
description: "Minimal A/B test plugin",
register(api: OpenClawPluginApi) {
api.logger?.info("[dummy-test:DIAG] register() ENTERED");
api.registerTool(
((ctx) => {
api.logger?.info(
`[dummy-test:DIAG] FACTORY called sandboxed=${ctx.sandboxed}`,
);
if (ctx.sandboxed) return null;
const tool: AnyAgentTool = {
name: "dummy-test",
label: "Dummy Test",
description: "Returns 'pong' to verify tool exposure.",
parameters: { type: "object", properties: {}, additionalProperties: false },
async execute() {
return {
content: [{ type: "text" as const, text: "pong" }],
details: { ok: true },
};
},
} as unknown as AnyAgentTool;
return tool;
}) as OpenClawPluginToolFactory,
{ optional: true },
);
api.logger?.info("[dummy-test:DIAG] register() EXITED");
},
});
openclaw.json (relevant snippets):
Expected (works on v2026.4.29)
Gateway log shows both DIAG lines on startup AND on first agent request:
[dummy-test:DIAG] register() ENTERED
[dummy-test:DIAG] register() EXITED
[dummy-test:DIAG] FACTORY called sandboxed=false ← appears at first agent request
Agent invocation of dummy-test returns "pong".
Actual (broken on v2026.5.2)
Gateway log shows only the register() lines on startup. The FACTORY called line never appears, no matter how many agent requests are made:
[dummy-test:DIAG] register() ENTERED
[dummy-test:DIAG] register() EXITED
# ... agent requests come and go, FACTORY never called ...
Agent invocation fails with unknown method: dummy-test.run.
Root cause
PR #76004 (commit 8283c5d6cc, "perf(plugins): reuse startup runtime registry") rewrote resolvePluginToolRegistry in src/plugins/tools.ts.
Before (v2026.4.29):
function resolvePluginToolRegistry(params) {
// 1. Try channel registry (if gateway-bindable / pinned)
// 2. Try active registry
// 3. FALLBACK: load on demand via jiti
return resolveRuntimePluginRegistry(params.loadOptions); // ← removed
}
After (v2026.5.2):
function resolvePluginToolRegistry(params) {
return (
getLoadedRuntimePluginRegistry({ ..., surface: "channel" }) ??
getLoadedRuntimePluginRegistry({ ..., surface: "active" })
);
// ← no fallback; returns undefined if neither has the plugin
}
And in resolvePluginTools:
const registry = resolvePluginToolRegistry({ loadOptions, onlyPluginIds: runtimePluginIds });
if (!registry) {
return tools; // ← bails silently, factory never invoked
}
Why bundled plugins still work: Bundled extensions (lobster, llm-task, memory-core) are loaded into the channel registry at gateway startup via bundled-channel-runtime,
so they're always present when surface: "channel" is queried.
Why path-based plugins don't: Plugins loaded via plugins.load.paths (manifest origin: "config") have their register() callback invoked during a separate discovery pass that
doesn't promote them to the channel registry with status: "loaded". The pre-2026.5.2 fallback resolveRuntimePluginRegistry(params.loadOptions) would load them on demand. With the
fallback removed, they're invisible to resolvePluginTools.
Same regression family — already partially fixed
The maintainers have already acknowledged and fixed this exact pattern in two adjacent surfaces:
Neither patches src/plugins/tools.ts, so the tool resolution surface — used by every agent on every turn — is still broken.
Likely-related issues that may be the same bug from different angles:
Workaround (in production today)
Pin to v2026.4.29. Patching the installed dist/plugins/tools.js with the diff above also works as a stopgap (verified locally — FACTORY called appears, agent returns "pong").
Acceptance / regression coverage
A regression test should ensure that a path-based plugin (origin "config") with a registered tool factory has that factory invoked when an agent resolves its tool list — even when
the channel and active registries are cold for that plugin id. The dummy-test plugin above can serve as the integration fixture.
OpenClaw version
2026.05.02
Operating system
Linux (Ubuntu)
Install method
No response
Model
gemini
Provider / routing chain
openclaw
Additional provider/model setup details
No response
Logs, screenshots, and evidence
Impact and severity
No response
Additional information
No response
Bug type
Regression (worked before, now fails)
Beta release blocker
No
Summary
Summary
After upgrading from
v2026.4.29tov2026.5.2, agent tools registered by path-based plugins (loaded viaplugins.load.paths, manifest origin"config") silently fail to appearin agent tool palettes. The plugin's
register()callback is invoked at gateway startup, but the tool factory is never called when an agent resolves its tool list, so the gatewayreturns
unknown methoderrors when the agent tries to call the tool.This is the same regression family that PR #76536 fixes for capability providers and PR #76393 fixed for memory-plugin doctor/status — the on-demand load fallback that PR #76004
("perf(plugins): reuse startup runtime registry") removed from
resolvePluginToolRegistryinsrc/plugins/tools.tsis also needed for tool resolution.The Codex review on PR #76004 explicitly flagged this exact risk before merge:
Steps to reproduce
Symptom
Gateway logs:
The agent thinks the tool isn't registered and skips/fails the call. No warning, no diagnostic — silent failure.
Minimal reproducer
A 40-line plugin that demonstrates the bug deterministically. Drop into a directory listed under
plugins.load.paths, allowlistdummy-testin your config, restart the gateway, andask any agent to invoke the tool.
dummy-test/package.json:{ "name": "@example/dummy-test", "version": "0.1.0", "type": "module", "openclaw": { "extensions": ["./index.ts"], "compat": { "pluginApi": ">=2026.3.24-beta.2" } } }dummy-test/openclaw.plugin.json:{ "id": "dummy-test", "name": "Dummy Test", "description": "Minimal plugin to verify path-based tool plugins work.", "activation": { "onStartup": true }, "contracts": { "tools": ["dummy-test"] }, "configSchema": { "type": "object", "additionalProperties": false, "properties": {} } }dummy-test/index.ts:openclaw.json(relevant snippets):{ "plugins": { "allow": ["dummy-test"], "load": { "paths": ["/path/to/dummy-test"] }, "entries": { "dummy-test": { "enabled": true } } }, "tools": { "allow": ["dummy-test", "group:plugins"] } }Expected (works on
v2026.4.29)Gateway log shows both
DIAGlines on startup AND on first agent request:Agent invocation of
dummy-testreturns"pong".Actual (broken on
v2026.5.2)Gateway log shows only the
register()lines on startup. TheFACTORY calledline never appears, no matter how many agent requests are made:Agent invocation fails with
unknown method: dummy-test.run.Expected behavior
Expected (works on
v2026.4.29)Gateway log shows both
DIAGlines on startup AND on first agent request:Agent invocation of
dummy-testreturns"pong".Actual behavior
Summary
After upgrading from
v2026.4.29tov2026.5.2, agent tools registered by path-based plugins (loaded viaplugins.load.paths, manifest origin"config") silently fail to appearin agent tool palettes. The plugin's
register()callback is invoked at gateway startup, but the tool factory is never called when an agent resolves its tool list, so the gatewayreturns
unknown methoderrors when the agent tries to call the tool.This is the same regression family that PR #76536 fixes for capability providers and PR #76393 fixed for memory-plugin doctor/status — the on-demand load fallback that PR #76004
("perf(plugins): reuse startup runtime registry") removed from
resolvePluginToolRegistryinsrc/plugins/tools.tsis also needed for tool resolution.The Codex review on PR #76004 explicitly flagged this exact risk before merge:
This issue is a real-world repro of that warning.
Affected versions
v2026.5.2-beta.2,v2026.5.2v2026.4.29(rollback fixes the issue immediately)Environment
plugins.load.pathsactivation.onStartup: trueandcontracts.tools: ["<tool-name>"]openclaw.json(bothplugins.allowandtools.allowandagent.tools.allow)Symptom
Gateway logs:
The agent thinks the tool isn't registered and skips/fails the call. No warning, no diagnostic — silent failure.
Minimal reproducer
A 40-line plugin that demonstrates the bug deterministically. Drop into a directory listed under
plugins.load.paths, allowlistdummy-testin your config, restart the gateway, andask any agent to invoke the tool.
dummy-test/package.json:{ "name": "@example/dummy-test", "version": "0.1.0", "type": "module", "openclaw": { "extensions": ["./index.ts"], "compat": { "pluginApi": ">=2026.3.24-beta.2" } } }dummy-test/openclaw.plugin.json:{ "id": "dummy-test", "name": "Dummy Test", "description": "Minimal plugin to verify path-based tool plugins work.", "activation": { "onStartup": true }, "contracts": { "tools": ["dummy-test"] }, "configSchema": { "type": "object", "additionalProperties": false, "properties": {} } }dummy-test/index.ts:openclaw.json(relevant snippets):{ "plugins": { "allow": ["dummy-test"], "load": { "paths": ["/path/to/dummy-test"] }, "entries": { "dummy-test": { "enabled": true } } }, "tools": { "allow": ["dummy-test", "group:plugins"] } }Expected (works on
v2026.4.29)Gateway log shows both
DIAGlines on startup AND on first agent request:Agent invocation of
dummy-testreturns"pong".Actual (broken on
v2026.5.2)Gateway log shows only the
register()lines on startup. TheFACTORY calledline never appears, no matter how many agent requests are made:Agent invocation fails with
unknown method: dummy-test.run.Root cause
PR #76004 (commit
8283c5d6cc, "perf(plugins): reuse startup runtime registry") rewroteresolvePluginToolRegistryinsrc/plugins/tools.ts.Before (v2026.4.29):
After (v2026.5.2):
And in
resolvePluginTools:Why bundled plugins still work: Bundled extensions (
lobster,llm-task,memory-core) are loaded into the channel registry at gateway startup viabundled-channel-runtime,so they're always present when
surface: "channel"is queried.Why path-based plugins don't: Plugins loaded via
plugins.load.paths(manifestorigin: "config") have theirregister()callback invoked during a separate discovery pass thatdoesn't promote them to the channel registry with
status: "loaded". The pre-2026.5.2 fallbackresolveRuntimePluginRegistry(params.loadOptions)would load them on demand. With thefallback removed, they're invisible to
resolvePluginTools.Same regression family — already partially fixed
The maintainers have already acknowledged and fixed this exact pattern in two adjacent surfaces:
capability-provider-runtime.ts. Description quotes verbatim:Neither patches
src/plugins/tools.ts, so the tool resolution surface — used by every agent on every turn — is still broken.Likely-related issues that may be the same bug from different angles:
clawsweeper[bot]referencing an unrelated commit; probably should be reopened.tools.profile: full) — different code path (allowlist filtering), but confirms multiple tool-resolution regressions in 2026.5.2.Workaround (in production today)
Pin to
v2026.4.29. Patching the installeddist/plugins/tools.jswith the diff above also works as a stopgap (verified locally —FACTORY calledappears, agent returns"pong").Acceptance / regression coverage
A regression test should ensure that a path-based plugin (origin
"config") with a registered tool factory has that factory invoked when an agent resolves its tool list — even whenthe channel and active registries are cold for that plugin id. The dummy-test plugin above can serve as the integration fixture.
OpenClaw version
2026.05.02
Operating system
Linux (Ubuntu)
Install method
No response
Model
gemini
Provider / routing chain
openclaw
Additional provider/model setup details
No response
Logs, screenshots, and evidence
Impact and severity
No response
Additional information
No response