-
-
Notifications
You must be signed in to change notification settings - Fork 69.4k
[Bug]: Gateway WS can return unknown method for plugin gateway methods visible in inspect/listings #54783
Description
Bug type
Behavior bug (incorrect output/state without crash)
Summary
Plugin gateway methods can be visible in plugins inspect / gateway method listings while the live gateway WebSocket still returns unknown method, which strongly suggests the WS handler table can diverge from the active plugin registry after secondary plugin loads / runtime registry changes.
Steps to reproduce
- Run a plugin-backed gateway method provider (repro observed with the
bncrchannel plugin exposingbncr.connect,bncr.inbound, etc.). - Confirm the plugin is loaded:
openclaw plugins inspect bncr
- Confirm the gateway method is listed in plugin metadata / gateway method output.
- In the failure state, call the live gateway over WS (or use the affected client flow) and observe:
unknown method: bncr.connect
- Restarting the gateway restores behavior temporarily.
Expected behavior
If a plugin gateway method is listed in the loaded plugin metadata / gateway method listings, the live gateway WebSocket should dispatch to the same method handler instead of returning unknown method.
Actual behavior
Observed failure mode:
openclaw plugins inspect bncrshows the plugin as loaded and lists:bncr.connectbncr.inboundbncr.activitybncr.ackbncr.diagnosticsbncr.file.*
- In the broken state, the client still receives:
unknown method: bncr.connect
- Gateway restart restores the method temporarily.
This looks like a process-level registry / handler divergence rather than a session-level issue.
OpenClaw version
2026.3.23-2 (7ffe7e4)
Operating system
Debian Linux (x64)
Install method
npm global
Model
N/A (gateway/plugin registry issue)
Provider / routing chain
Gateway WS → plugin-backed channel method (bncr.connect)
Additional provider/model setup details
The affected plugin is loaded from a local workspace path:
~/.openclaw/workspace/plugins/bncr/index.ts
The plugin itself was hardened locally to make registration idempotent per API instance and to rebind its singleton bridge to the current API on re-registration. That reduced plugin-side ambiguity but does not eliminate the host-side failure mode.
Logs, screenshots, and evidence
Evidence 1: live gateway dispatch returns unknown method based on extraHandlers
From the compiled gateway code (dist/gateway-cli-*.js):
const handler = opts.extraHandlers?.[req.method] ?? coreGatewayHandlers[req.method];
if (!handler) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `unknown method: ${req.method}`));
return;
}This means the live WS path depends on extraHandlers, not just on method listings.
Evidence 2: method listing comes from a different source
Also from the compiled gateway code:
function listGatewayMethods() {
const channelMethods = listChannelPlugins().flatMap((plugin) => plugin.gatewayMethods ?? []);
return Array.from(new Set([...BASE_METHODS, ...channelMethods]));
}This means the method list is built from plugin declarations / channel plugin metadata, not from the same extraHandlers object used by WS dispatch.
Evidence 3: WS extraHandlers is constructed from a plugin registry snapshot
extraHandlers: {
...pluginRegistry.gatewayHandlers,
...execApprovalHandlers,
...secretsHandlers
}Because this spreads pluginRegistry.gatewayHandlers into a new object during gateway setup, it appears to behave like a snapshot rather than a live view of the registry.
Evidence 4: active plugin registry is globally replaceable
From runtime code:
function setActivePluginRegistry(registry, cacheKey) {
state.registry = registry;
state.key = cacheKey ?? null;
state.version += 1;
}So the active registry can change later, while the WS handler table may still be using an earlier expanded extraHandlers object.
Evidence 5: resolvePluginTools() can conditionally trigger a plugin reload path
const activeRegistry = getActivePluginRegistry();
const registry = getActivePluginRegistryKey() && activeRegistry ? activeRegistry : loadOpenClawPlugins(...);This suggests certain secondary paths (tool discovery / agent tool resolution / related runtime flows) can still take a reload path instead of always reusing the current active registry.
Local verification done
On the affected host, the following remained healthy in the non-broken state:
openclaw plugins inspect bncr→ loaded, methods presentopenclaw gateway call bncr.connect --json --params '{}'→ succeeds when healthyopenclaw gateway call tools.catalog --json --params '{"agentId":"main","includePlugins":true}'alone did not immediately reproduce the failure
So this does not appear to be “every plugin tool lookup always breaks the registry”, but rather a more conditional runtime divergence.
Impact and severity
- Affected: plugin-backed gateway methods / channel methods
- Severity: High when it happens, because a loaded plugin can become partially unreachable through the live gateway WS API
- Consequence: channel/client links can fail even though plugin inspection still looks healthy
Additional information
This appears closely related to existing registry-divergence issues and fixes:
- [Bug]: Plugin-backed channels can become unknown in subagent flows when OPENCLAW_STATE_DIR is relative #52250 — plugin-backed channels become unknown after secondary plugin load paths
- [Bug]: Plugin tools (registerTool) permanently lost after image message: gateway-level registry corruption #53494 — plugin tools remain listed/loaded but become unavailable at runtime after image-related flows
- fix: use live plugin registry for HTTP route matching after reload #45606 — HTTP route matching used a stale/captured plugin registry rather than the live one
- Gateway: rebind plugin registry before channel lifecycle #49526 — gateway/channel lifecycle needs plugin registry rebinding
- fix(plugins): deduplicate tool registration to prevent double-register #52592 — duplicate plugin registration mitigation in the registry layer
The pattern looks similar to #45606, but for gateway WS methods instead of HTTP routes: runtime dispatch appears to consume a handler table that can diverge from the later active plugin registry / declared method listings.
Suggested fix direction
Likely one of these (or a combination):
- Make WS method dispatch consult the live plugin gateway handler registry instead of a startup-time expanded
extraHandlerssnapshot. - Rebind / refresh the gateway WS plugin handler table whenever the active plugin registry changes.
- Audit secondary
loadOpenClawPlugins()paths to ensure they do not replace the active runtime registry used by live gateway dispatch unless all dependent runtime tables are updated too.