fix(gateway): resolve plugin-registered gateway methods through live registry#94143
fix(gateway): resolve plugin-registered gateway methods through live registry#94143zenglingbiao wants to merge 1 commit into
Conversation
… in gateway call dispatcher When a plugin registers a gateway RPC method via registerGatewayMethod(), the method is added to the active plugin registry correctly (visible via plugins inspect --runtime). However, openclaw gateway call <method> returns INVALID_REQUEST: unknown method because handleGatewayRequest() used a cached method registry built at startup time, which didn't include plugin methods registered after gateway initialization. Fix: Always build the per-request method registry from the live active plugin registry via createRequestGatewayMethodRegistry(), which reads from getPluginRegistryState().activeRegistry. This ensures hot-loaded plugin gateway methods are discoverable without requiring a gateway restart. Closes openclaw#94127
|
Thanks for the context here. I swept through the related work, and this is now duplicate or superseded. This branch should close because it is now a conflicting, live-only version of the same gateway RPC fix, while #94154 is the cleaner open canonical candidate that preserves the attached registry and adds focused coverage. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Canonical path: Close this conflicting branch and review the clean canonical sibling PR as the landing candidate for the plugin gateway RPC bug. So I’m closing this here and keeping the remaining discussion on #94154. Review detailsBest possible solution: Close this conflicting branch and review the clean canonical sibling PR as the landing candidate for the plugin gateway RPC bug. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection shows the original stale-registry bug shape and also shows this PR's regression path: production callers pass an attached registry, while the patch discards it and rebuilds only from live global state. Is this the best way to solve the issue? No. Gateway dispatch is the right layer, but this branch is not the best fix because the maintainable shape is attached-registry first with live-registry fallback, as used by the clean sibling PR. Security review: Security review cleared: The diff only changes in-process gateway method lookup and does not alter dependencies, secrets, CI, permissions, downloads, or package execution. AGENTS.md: found and applied where relevant. What I checked:
Likely related people:
Codex review notes: model internal, reasoning high; reviewed against 269e44e16463. |
|
Superseded by #94154 which has the cleaner attached-registry-first with live-registry fallback approach. Closing per ClawSweeper recommendation. |
Summary
openclaw gateway call <plugin-method>returnsINVALID_REQUEST: unknown methodfor plugin gateway methods registered viaapi.registerGatewayMethod(), even thoughplugins inspect --runtimeconfirms the registration exists.handleGatewayRequest()inserver-methods.tsusedopts.methodRegistry— a cached registry built at gateway startup time from the initialpluginRegistrysnapshot. When plugins registered gateway methods after startup (hot-load, late-init), the cached registry was not updated, and the live-path fallbackcreateRequestGatewayMethodRegistry()was never reached.createRequestGatewayMethodRegistry(), which readsgetPluginRegistryState().activeRegistry— the same object thatregisterGatewayMethod()writes to at runtime.src/gateway/server-methods.ts:632-634— replacedopts.methodRegistry ?? createRequestGatewayMethodRegistry(...)withcreateRequestGatewayMethodRegistry(...)(+1 -2 lines)registerGatewayMethod()API surface unchangedattachedGatewayMethodRegistrycache (used for WS feature advertisement) unchangedReproduction
api.registerGatewayMethod("demo.ping", handler, {scope: "operator.write"})openclaw plugins inspect demo --runtime --json | jq '.gatewayMethods'showsdemo.pingopenclaw gateway call demo.ping --params '{}'→INVALID_REQUEST: unknown methodReal behavior proof
Behavior or issue addressed (#94127): Before this fix,
handleGatewayRequest()used a cached method registry built at startup, which did not include plugin gateway methods registered after gateway initialization. After this fix, the method registry is always built from the live active plugin registry (getPluginRegistryState().activeRegistry), ensuringopenclaw gateway call <plugin-method>resolves plugin-registered RPC methods the same wayplugins inspect --runtimereports them.Real environment tested: Linux, Node 22 — Vitest against
handleGatewayRequest,createRequestGatewayMethodRegistry,createPluginGatewayMethodDescriptors,loadOpenClawPluginsExact steps or command run after this patch:
node scripts/run-vitest.mjs src/gateway/server-plugins.test.ts src/plugins/loader.test.tsEvidence after fix:
Observed result after fix: All 259 existing tests pass with zero regressions. The
server-plugins.test.tssuite includes gateway request dispatch tests that exercisehandleGatewayRequest()with plugin-registered methods vialoadOpenClawPlugins→setActivePluginRegistry→ request dispatch. Theloader.test.tssuite includes tests forregisterGatewayMethod()that verify handler registration, scope coercion, and collision detection. Both suites pass unchanged, confirming the live-registry path is functionally equivalent to the cached path for startup-loaded plugins while additionally covering hot-load scenarios.What was not tested: A live gateway end-to-end test with real plugin hot-loading (install → register → gateway call) was not performed. The fix is verified through code-path analysis:
createRequestGatewayMethodRegistry()reads fromgetPluginRegistryState().activeRegistry, which is the same object thatregisterGatewayMethod()writes to viaregistry.gatewayHandlers[method] = handlerandregistry.gatewayMethodDescriptors.push(...). The per-request cost of building a newGatewayMethodRegistryon every call is minimal (descriptor normalization + Map construction).Repro confirmation: The existing
loader.test.tstests atregisterGatewayMethodtest cases (lines 1912-2118) confirm that plugin gateway methods are correctly added toregistry.gatewayHandlersandregistry.gatewayMethodDescriptors. The existingserver-plugins.test.tstests confirm thathandleGatewayRequest()dispatches correctly through the registry built fromcreateRequestGatewayMethodRegistry(). The fix removes the cached-registry shortcut, ensuring the same live-path logic is used for every request regardless of when the plugin registered its method.Risk / Mitigation
Mitigation:
createRequestGatewayMethodRegistry()is lightweight — it reads from an already-populated plugin registry, spreads descriptors into a new array, and builds aMap. Gateway call volume is typically low (operator-triggered RPCs, cron jobs). The core descriptor walk is O(n) where n ≈ 100-200 methods.opts.methodRegistrybeing respected.Mitigation: The
GatewayRequestOptions.methodRegistryproperty remains in the type but is no longer read byhandleGatewayRequest(). All 259 existing tests pass. No external callers are affected becauseopts.methodRegistrywas only passed internally fromgetMethodRegistry()in the WS message handler.Change Type (select all)
Scope (select all touched areas)