Skip to content

fix: resolve plugin registry via getter injection to fix LINE webhook 404#45150

Closed
masatohoshino wants to merge 0 commit intoopenclaw:mainfrom
masatohoshino:fix/plugin-registry-getter
Closed

fix: resolve plugin registry via getter injection to fix LINE webhook 404#45150
masatohoshino wants to merge 0 commit intoopenclaw:mainfrom
masatohoshino:fix/plugin-registry-getter

Conversation

@masatohoshino
Copy link
Copy Markdown
Contributor

Summary

Fixes the LINE webhook 404 regression by replacing direct global plugin registry access with getter-based injection.

Closes #34631
Supersedes #40224

What changed

  • Extended createGatewayPluginRequestHandler with getRegistry param (getRegistry > registry priority, fail-fast if both missing)
  • Migrated runtime-state call sites to use a shared getter for handler/auth consistency
  • Introduced pluginRegistryCurrent in server.impl with getter-based injection

How #40224 review feedback is addressed

  1. Global dependency removed — registry is resolved via injected getter, not global state
  2. Call site migration complete — all 3 call sites updated (handler, auth callback, tests)
  3. Backward compatible — existing registry: param still works; getRegistry is additive

Tests added

  • Fail-fast when both getRegistry and registry are missing
  • Registry swap boundary (new request resolves to updated registry)
  • Global independence (injected getter is not affected by global state pollution)

Notes

  • Affects version 2026.3.12 — backport appreciated
  • All existing plugins-http.test.ts tests pass
  • pnpm tsgo passes clean

@openclaw-barnacle openclaw-barnacle bot added gateway Gateway runtime size: S labels Mar 13, 2026
@Ryce

This comment was marked as spam.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 13, 2026

Greptile Summary

This PR refactors how the plugin registry is supplied to createGatewayPluginRequestHandler and shouldEnforceGatewayAuthForPluginPath, replacing a direct value capture with a getRegistry getter so the registry can be resolved on each request invocation rather than once at construction time. The handler-level implementation in plugins-http.ts is correct and well-tested.

However, the critical piece in server.impl.ts is incomplete:

  • pluginRegistryCurrent is introduced as a let variable but is never reassigned anywhere in the file. The getter () => pluginRegistryCurrent will therefore always return initialPluginRegistry — identical in behavior to the old direct-value approach. If the LINE webhook 404 is caused by a stale registry that is updated asynchronously after server startup, the fix does not resolve it because no code updates pluginRegistryCurrent after initialization.
  • The extraHandlers spread (line 892) and startGatewaySidecars call (line 933) correctly keep using initialPluginRegistry, which is consistent since those are setup-time snapshots.
  • Auth-gate (shouldEnforcePluginGatewayAuth) and dispatch (handlePluginRequest) call the getter independently per request, which could observe different registry snapshots if the registry is ever mutated concurrently.

The new tests are well-structured and accurately cover the handler-level contract, but they don't catch the missing pluginRegistryCurrent assignment in the server implementation.

Confidence Score: 2/5

  • The handler-level change is safe, but the server.impl.ts change is a no-op at runtime because pluginRegistryCurrent is never reassigned — meaning the stated fix for the LINE webhook 404 is not actually applied.
  • The core architectural pattern (getter injection) is correct and plugins-http.ts is well-implemented and tested. However, pluginRegistryCurrent being declared as let but never mutated makes the change in server.impl.ts functionally equivalent to the old code. If the regression requires the registry to be updated at runtime, that update path is entirely missing. This is a logic gap that blocks the PR from achieving its stated purpose.
  • src/gateway/server.impl.ts — the pluginRegistryCurrent variable needs the code path that reassigns it to the post-load registry value.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/gateway/server.impl.ts
Line: 481

Comment:
**`pluginRegistryCurrent` is never reassigned — getter always returns `initialPluginRegistry`**

`pluginRegistryCurrent` is declared with `let` on this line and the getter `() => pluginRegistryCurrent` is wired in at line 621, but there is no code anywhere in `server.impl.ts` that ever reassigns `pluginRegistryCurrent` to a new value. This means:

1. The getter `() => pluginRegistryCurrent` is functionally identical to using `registry: initialPluginRegistry` directly.
2. The "registry swap boundary" capability that the new `getRegistry` pattern enables in `createGatewayPluginRequestHandler` is never actually exercised at runtime.

If the root cause of the LINE webhook 404 regression is a stale plugin registry (e.g., because the registry is populated asynchronously after the HTTP server starts listening), the fix is incomplete — you need the code path that updates `pluginRegistryCurrent` to the post-load registry. Without it, callers will always see the registry as it was at startup.

If the intent is to wire in a future hot-reload mechanism, a comment explaining this would help avoid confusion. If `pluginRegistryCurrent` is genuinely expected to be mutated by some future call site, that reassignment is missing from this PR.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/gateway/server-runtime-state.ts
Line: 131-133

Comment:
**`shouldEnforcePluginGatewayAuth` and `handlePluginRequest` call `getRegistry` independently**

For a single incoming request, `shouldEnforcePluginGatewayAuth` (called during auth gate) and `handlePluginRequest` (called during dispatch) each invoke the getter independently. If `pluginRegistryCurrent` were ever mutated between these two calls on the same request, the auth check and the route-matching step could observe different registries — e.g., a route is deemed "does not require auth" by the auth gate but then matched to a gateway-auth route by the handler (or vice versa).

Consider capturing the registry once per request (e.g., pass the snapshot through to both) rather than calling the getter twice.

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 222708f

@masatohoshino
Copy link
Copy Markdown
Contributor Author

Thanks @George-Claw for the detailed analysis in #45445.

This PR addresses Issue 1 (stale registry capture via getter injection). Issue 2 (httpRoutes lost on registry replacement in setActivePluginRegistry) requires a different fix and is outside the scope of this PR. #45445 tracks both issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Title: LINE webhook route intermittently returns 404 until gateway restart (2026.3.2)

2 participants