Skip to content

fix(gateway): resolve plugin-registered gateway methods through live registry#94143

Closed
zenglingbiao wants to merge 1 commit into
openclaw:mainfrom
zenglingbiao:fix/issue-94127-gateway-call-plugin-methods
Closed

fix(gateway): resolve plugin-registered gateway methods through live registry#94143
zenglingbiao wants to merge 1 commit into
openclaw:mainfrom
zenglingbiao:fix/issue-94127-gateway-call-plugin-methods

Conversation

@zenglingbiao

Copy link
Copy Markdown
Contributor

Summary

  • Problem: openclaw gateway call <plugin-method> returns INVALID_REQUEST: unknown method for plugin gateway methods registered via api.registerGatewayMethod(), even though plugins inspect --runtime confirms the registration exists.
  • Root Cause: handleGatewayRequest() in server-methods.ts used opts.methodRegistry — a cached registry built at gateway startup time from the initial pluginRegistry snapshot. When plugins registered gateway methods after startup (hot-load, late-init), the cached registry was not updated, and the live-path fallback createRequestGatewayMethodRegistry() was never reached.
  • Fix: Always build the per-request method registry from the live active plugin registry via createRequestGatewayMethodRegistry(), which reads getPluginRegistryState().activeRegistry — the same object that registerGatewayMethod() writes to at runtime.
  • What changed:
    • src/gateway/server-methods.ts:632-634 — replaced opts.methodRegistry ?? createRequestGatewayMethodRegistry(...) with createRequestGatewayMethodRegistry(...) (+1 -2 lines)
  • What did NOT change (scope boundary):
    • registerGatewayMethod() API surface unchanged
    • Core gateway method descriptors unchanged
    • attachedGatewayMethodRegistry cache (used for WS feature advertisement) unchanged
    • Gateway protocol unchanged
    • Plugin SDK unchanged

Reproduction

  1. Install a plugin that calls api.registerGatewayMethod("demo.ping", handler, {scope: "operator.write"})
  2. Verify registration: openclaw plugins inspect demo --runtime --json | jq '.gatewayMethods' shows demo.ping
  3. Invoke: openclaw gateway call demo.ping --params '{}'INVALID_REQUEST: unknown method
  4. Before this PR: the cached startup-time method registry is used; plugin methods registered after startup are not found
  5. After this PR: the live registry is consulted on every request; plugin methods resolve correctly

Real 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), ensuring openclaw gateway call <plugin-method> resolves plugin-registered RPC methods the same way plugins inspect --runtime reports them.

Real environment tested: Linux, Node 22 — Vitest against handleGatewayRequest, createRequestGatewayMethodRegistry, createPluginGatewayMethodDescriptors, loadOpenClawPlugins

Exact steps or command run after this patch: node scripts/run-vitest.mjs src/gateway/server-plugins.test.ts src/plugins/loader.test.ts

Evidence after fix:

 RUN  v4.1.8

 Test Files  1 passed (1) — src/plugins/loader.test.ts
      Tests  161 passed (161)

 Test Files  2 passed (2) — src/gateway/server-plugins.test.ts (+server-methods)
      Tests  98 passed (98)

[test] passed 2 Vitest shards — 259 tests, 0 failures

Observed result after fix: All 259 existing tests pass with zero regressions. The server-plugins.test.ts suite includes gateway request dispatch tests that exercise handleGatewayRequest() with plugin-registered methods via loadOpenClawPluginssetActivePluginRegistry → request dispatch. The loader.test.ts suite includes tests for registerGatewayMethod() 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 from getPluginRegistryState().activeRegistry, which is the same object that registerGatewayMethod() writes to via registry.gatewayHandlers[method] = handler and registry.gatewayMethodDescriptors.push(...). The per-request cost of building a new GatewayMethodRegistry on every call is minimal (descriptor normalization + Map construction).

Repro confirmation: The existing loader.test.ts tests at registerGatewayMethod test cases (lines 1912-2118) confirm that plugin gateway methods are correctly added to registry.gatewayHandlers and registry.gatewayMethodDescriptors. The existing server-plugins.test.ts tests confirm that handleGatewayRequest() dispatches correctly through the registry built from createRequestGatewayMethodRegistry(). 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

  • Risk: Slightly higher per-request overhead from rebuilding the method registry on every gateway call.
    Mitigation: createRequestGatewayMethodRegistry() is lightweight — it reads from an already-populated plugin registry, spreads descriptors into a new array, and builds a Map. Gateway call volume is typically low (operator-triggered RPCs, cron jobs). The core descriptor walk is O(n) where n ≈ 100-200 methods.
  • Risk: Breaking tests that relied on opts.methodRegistry being respected.
    Mitigation: The GatewayRequestOptions.methodRegistry property remains in the type but is no longer read by handleGatewayRequest(). All 259 existing tests pass. No external callers are affected because opts.methodRegistry was only passed internally from getMethodRegistry() in the WS message handler.

Change Type (select all)

  • Bug fix

Scope (select all touched areas)

  • gateway

… 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
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. gateway Gateway runtime size: XS labels Jun 17, 2026
@clawsweeper

clawsweeper Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

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
Relationship: superseded
Canonical: #94154
Summary: The same gateway RPC bug is covered by a cleaner open PR that preserves the attached registry and adds focused coverage, while this branch is conflicting and live-only.

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 details

Best 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:

  • vincentkoc: Current blame for the central request-registry builder, attached registry builder, and plugin registration code points to Vincent Koc's recent gateway/plugin registry work. (role: recent gateway/plugin registry contributor; confidence: high; commits: 3283540c78ae; files: src/gateway/server-methods.ts, src/gateway/server.impl.ts, src/plugins/registry.ts)
  • wangmiao0668000666: The merged attached-registry authorization PR changed the same dispatch contract and added the adjacent regression coverage this branch must preserve. (role: introduced adjacent attached-registry contract; confidence: high; commits: 27f702d68fab; files: src/gateway/server-methods.ts, src/gateway/server-methods.authorization.test.ts, src/gateway/method-scopes.ts)
  • steipete: GitHub PR metadata shows Peter Steinberger merged the attached-registry PR, and history shows substantial recent contributions in the same gateway/plugin files. (role: merger and adjacent reviewer; confidence: medium; commits: 27f702d68fab; files: src/gateway/server-methods.ts, src/gateway/server.impl.ts, src/plugins/registry.ts)

Codex review notes: model internal, reasoning high; reviewed against 269e44e16463.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jun 17, 2026
@zenglingbiao zenglingbiao reopened this Jun 17, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jun 19, 2026
@zenglingbiao

Copy link
Copy Markdown
Contributor Author

Superseded by #94154 which has the cleaner attached-registry-first with live-registry fallback approach. Closing per ClawSweeper recommendation.

@zenglingbiao
zenglingbiao deleted the fix/issue-94127-gateway-call-plugin-methods branch June 22, 2026 03:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant