fix(gateway): pin channel registry at startup to survive registry swaps#53944
Conversation
Greptile SummaryThis PR fixes a production regression (38 Key changes:
One minor point: The returned Confidence Score: 5/5
Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/gateway/server-runtime-state.ts
Line: 233-237
Comment:
**`releasePluginRouteRegistry` now releases channel pin too**
The returned state property `releasePluginRouteRegistry` silently expanded its scope: it now releases both the HTTP route registry pin and the channel registry pin. The name only reflects route-registry concerns, so a future reader may not realise that calling (or skipping) this function also controls channel resolution stability.
Consider renaming the property to `releasePluginRegistryPins` (or similar) to reflect its broadened responsibility, or add a short inline comment noting the dual release:
```suggestion
releasePluginRouteRegistry: () => {
// Releases both pinned HTTP-route and channel registries set at startup.
releasePinnedPluginHttpRouteRegistry(params.pluginRegistry);
releasePinnedPluginChannelRegistry(params.pluginRegistry);
},
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix(gateway): pin channel registry at st..." | Re-trigger Greptile |
94c1055 to
dd5c9d8
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 313587dcbd
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9643f6be38
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
da8dc45 to
8fc74e8
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 40566892b1
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
4056689 to
17bb1eb
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 17bb1eb402
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2064d98d99
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c2a2d23780
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 60b8e1b05e
ℹ️ 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".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a1f1bb5ee0
ℹ️ 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".
Channel plugin resolution fails with 'Channel is unavailable: <channel>' after the active plugin registry is replaced at runtime. The root cause is that getChannelPlugin() resolves against the live registry snapshot, which is replaced when non-primary registry loads (e.g., config-schema reads) call loadOpenClawPlugins(). If the replacement registry does not carry the same channel entries, outbound message delivery and subagent announce silently break. This mirrors the existing pinActivePluginHttpRouteRegistry pattern: the channel registry is pinned at gateway startup and released on shutdown. Subsequent setActivePluginRegistry calls no longer evict the channel snapshot, so getChannelPlugin() always resolves against the registry that was active when the gateway booted.
Address Greptile review: releasePluginRouteRegistry now releases both HTTP-route and channel registry pins. Added comment for clarity.
When preferSetupRuntimeForChannelPlugins is active, gateway boot performs two plugin loads: a setup-runtime pass and a full reload after listen. The initial pin captured the setup-entry snapshot. The deferred reload now re-pins so getChannelPlugin() resolves against the full implementations.
a1f1bb5 to
3daf74c
Compare
|
Landed via temp rebase onto main.
Thanks @affsantos! |
Summary
getChannelPlugin()returnsundefinedafter a runtime registry swap, causingChannel is unavailable: <channel>errors on all outbound message delivery — themessagetool, subagent completion announces, and cron delivery. The channel is known (isKnownChannel()passes via the hardcodedCHANNEL_IDSarray) but the plugin backing it is gone from the live registry.Channel is unavailable: slackerrors/day and 72 subagent announce failures/day — up from zero on the previous version. Reproduced and confirmed still present on 2026.3.23-2. Users see dropped replies and undelivered subagent results with no feedback. The issue affects all channels (Slack, Telegram, WhatsApp, Discord) — see Related Issues below.loadOpenClawPlugins()calls (config-schema reads, provider registry lookups,maybeBootstrapChannelPluginfallback) can replace the active plugin registry viasetActivePluginRegistry(). The replacement registry may have a different channel plugin set — or none at all — because it was built with different loader options.getChannelPlugin()inchannels/plugins/registry.tsresolves against the liverequireActivePluginRegistry(), so it immediately loses access to the channels that were present at gateway boot.runtime.ts) mirroring the existingpinActivePluginHttpRouteRegistrypattern. The channel registry is pinned at gateway startup inserver-runtime-state.tsand released on shutdown.resolveCachedChannelPlugins()now reads fromrequireActivePluginChannelRegistry()instead ofrequireActivePluginRegistry(), so channel resolution is immune to mid-flight registry swaps.resolveOutboundChannelPluginbootstrap path still works as before — it just no longer needs to fire because the pinned registry already has the channels.🤖 AI-assisted
Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Channel is unavailableregression in 2026.3.22)Channel is unavailablein 2026.3.23-2)Unsupported channel: slack)channel_idnot passed to tool context)User-visible / Behavior Changes
Channel plugins registered at gateway startup are no longer evicted by subsequent plugin registry swaps. The
messagetool, subagent announces, cron delivery, and all other outbound paths that resolve channels viagetChannelPlugin()will consistently find the channel plugin as long as the gateway is running.Security Impact (required)
NoNoNoNoNoRepro + Verification
Environment
Steps
Expected
Actual (before fix)
getChannelPlugin("slack")returnsundefined[tools] message failed: Channel is unavailable: slackSubagent completion direct announce failed: Error: Unknown channel: slackEvidence from production
Before (2026.3.22, no pin) — 7-day daily error count:
Log pattern (cascading announce failures):
Affected session types:
slack:channel:*— channel thread repliesslack:direct:*— DM repliessubagent:*— subagent completion announcesopenai:*— MCP/API sessions attempting Slack deliveryTests
7 new tests covering pin/release/swap lifecycle:
Full test run: 7 new + 613 existing channel/outbound/plugin tests pass.
Co-Authored: Mário Sousa (@mariosousa-finn)