fix(line): use getRegistry callback in plugin HTTP handler to prevent stale-route 404#40224
fix(line): use getRegistry callback in plugin HTTP handler to prevent stale-route 404#40224joelnishanth wants to merge 3 commits intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR fixes a stale-registry 404 bug where the gateway HTTP handler held a captured reference to the initial The fix is clean and correct:
The core change (callback instead of captured reference) is correct, well-tested, and solves a real production issue with no behavior regressions. One minor cleanup item: the now-unused Confidence Score: 4/5
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cc4461706a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // with a fresh registry object) are always visible without a gateway restart. | ||
| const handlePluginRequest = createGatewayPluginRequestHandler({ | ||
| registry: params.pluginRegistry, | ||
| getRegistry: requireActivePluginRegistry, |
There was a problem hiding this comment.
Use the runtime-state registry instead of global plugin state
createGatewayRuntimeState now ignores params.pluginRegistry and always reads requireActivePluginRegistry(), which introduces a hidden global dependency and can serve stale routes in minimal test gateway mode. In startGatewayServer, minimalTestGateway explicitly bypasses loadGatewayPlugins and passes a fresh empty registry (src/gateway/server.impl.ts:429-431), so this change can pull in whatever registry was left in global plugin runtime state from earlier tests/process activity, making plugin route/auth behavior nondeterministic.
Useful? React with 👍 / 👎.
… stale-route 404 When a plugin config change triggers loadOpenClawPlugins with a different cache key, setActivePluginRegistry replaces the active registry with a new object. createGatewayPluginRequestHandler was capturing the original registry at startup, so any routes re-registered by channels (e.g. /line/webhook) on the new registry were invisible to the HTTP handler — returning 404 until a full gateway restart. Fix: accept a getRegistry callback instead of a pinned registry reference so the handler always resolves routes from the current active registry. Also update the shouldEnforcePluginGatewayAuth call in server-runtime-state.ts to use requireActivePluginRegistry() inline for the same reason. Adds a regression test (stale-registry swap) that fails without the fix and passes with it. Closes openclaw#34631
Made-with: Cursor
cc44617 to
a14a537
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a14a53753a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| * change (which replaces the active registry via setActivePluginRegistry) | ||
| * are always visible to the HTTP handler without a gateway restart. | ||
| */ | ||
| getRegistry: () => PluginRegistry; |
There was a problem hiding this comment.
Update remaining handler call sites for getRegistry
Making getRegistry required here leaves two existing invocations in src/gateway/server/plugins-http.test.ts still passing registry: ...; when those tests execute, the returned handler reaches getRegistry() with undefined and throws before the route/auth assertions run. This means the commit introduces immediate breakage for the plugin HTTP test coverage path (and corresponding type-checking jobs) until all callers are migrated to getRegistry: () => registry.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d6f203616c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // with a fresh registry object) are always visible without a gateway restart. | ||
| const handlePluginRequest = createGatewayPluginRequestHandler({ | ||
| registry: params.pluginRegistry, | ||
| getRegistry: requireActivePluginRegistry, |
There was a problem hiding this comment.
Use provided plugin registry when building runtime handlers
createGatewayRuntimeState now ignores params.pluginRegistry and always reads requireActivePluginRegistry(), which introduces a process-global dependency into request routing. In startGatewayServer minimal gateway mode intentionally skips loadGatewayPlugins and passes an explicit empty registry (src/gateway/server.impl.ts:470-472,616), so these handlers can instead read whatever registry was last written globally by other startup/tests, making plugin route matching and gateway-auth decisions nondeterministic in that mode.
Useful? React with 👍 / 👎.
|
Superseded by #45150, which addresses the review feedback from this PR:
Leaving this PR for maintainer to close at their discretion. |
|
Closing as stale. Will revisit if the issue resurfaces. |
Summary
loadOpenClawPluginsis called again with a different cache key, creating a newPluginRegistryand callingsetActivePluginRegistry(newRegistry). The gateway HTTP handler (createGatewayPluginRequestHandler) still holds a reference to the original registry from startup. The LINE channel then re-registers/line/webhookon the new active registry, but all incoming requests are matched against the old (now empty) registry — returning 404 until a full gateway restart.createGatewayPluginRequestHandlernow accepts agetRegistrycallback (() => PluginRegistry) instead of a pinnedregistryobject, and resolves the live registry on every request.shouldEnforceGatewayAuthForPluginPathinserver-runtime-state.tsis updated to callrequireActivePluginRegistry()inline as well.monitorLineProvider,registerPluginHttpRoute, channel lifecycle, auth enforcement behaviour.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
/line/webhook(and any other plugin-registered webhook path) remains reachable after plugin config edits without requiring a gateway restart.Security Impact (required)
Repro + Verification
Environment
Steps
openclaw config set plugins.X.enabled true/false(triggersloadOpenClawPluginswith a new cache key).curl -i http://127.0.0.1:18789/line/webhookExpected
Actual (before fix)
openclaw gateway restartEvidence
Human Verification (required)
loadOpenClawPlugins→setActivePluginRegistry→createGatewayPluginRequestHandlerin code review; confirmed that the capturedregistryreference goes stale on any cache-key change.Compatibility / Migration
Failure Recovery (if this breaks)
src/gateway/server/plugins-http.tsandsrc/gateway/server-runtime-state.tsto the captured-registry pattern.Risks and Mitigations
requireActivePluginRegistry()called on every HTTP request adds a negligible Map-lookup overhead.Note: This is a resubmission of #34646 (auto-closed due to PR queue limit). No other PRs exist for this issue.
— Joel Nishanth · offlyn.AI