fix(channels): use pinned channel registry for outbound adapter resolution#55202
Conversation
Greptile SummaryThis PR fixes a registry divergence bug where the channel outbound adapter loader ( Confidence Score: 5/5Safe to merge — minimal two-line fix with a direct regression test and no side-effects. The change is exactly the right fix: the outbound surface was the only remaining consumer of the unpinned registry, and the patch aligns it with the already-correct inbound path. The cache invalidation logic is unaffected. The new test directly reproduces the reported failure. All existing tests continue to pass per the test plan. No files require special attention.
|
| Filename | Overview |
|---|---|
| src/channels/plugins/registry-loader.ts | Two-line fix: replaces getActivePluginRegistry() with getActivePluginChannelRegistry() so the outbound adapter loader reads from the same pinned channel surface as the inbound path. Logic is correct and the cache invalidation still works properly. |
| src/plugins/runtime.channel-pin.test.ts | Adds a targeted regression test that pins a startup registry with a telegram outbound adapter, then replaces the active registry with an empty one, and asserts loadChannelOutboundAdapter still resolves the adapter. Directly covers the bug scenario. |
Comments Outside Diff (1)
-
src/channels/plugins/registry-loader.ts, line 17-20 (link)Cache invalidation still correct with pinned surface
The
registry !== lastRegistrycheck is fine under the new surface. When the channel registry is pinned,getActivePluginChannelRegistry()keeps returning the same object reference, so the cache is never spuriously cleared — which is an improvement over the old behaviour where everysetActivePluginRegistrycall (config-schema loads, etc.) would return a new object and bust the cache. When the channel registry is not yet pinned,syncTrackedSurfacekeepsstate.channel.registryin lock-step withstate.activeRegistry, so the cache still invalidates on legitimate registry swaps. No issues here, just noting the analysis for reviewers.Prompt To Fix With AI
This is a comment left during a code review. Path: src/channels/plugins/registry-loader.ts Line: 17-20 Comment: **Cache invalidation still correct with pinned surface** The `registry !== lastRegistry` check is fine under the new surface. When the channel registry is pinned, `getActivePluginChannelRegistry()` keeps returning the same object reference, so the cache is never spuriously cleared — which is an improvement over the old behaviour where every `setActivePluginRegistry` call (config-schema loads, etc.) would return a new object and bust the cache. When the channel registry is not yet pinned, `syncTrackedSurface` keeps `state.channel.registry` in lock-step with `state.activeRegistry`, so the cache still invalidates on legitimate registry swaps. No issues here, just noting the analysis for reviewers. How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/channels/plugins/registry-loader.ts
Line: 17-20
Comment:
**Cache invalidation still correct with pinned surface**
The `registry !== lastRegistry` check is fine under the new surface. When the channel registry is pinned, `getActivePluginChannelRegistry()` keeps returning the same object reference, so the cache is never spuriously cleared — which is an improvement over the old behaviour where every `setActivePluginRegistry` call (config-schema loads, etc.) would return a new object and bust the cache. When the channel registry is not yet pinned, `syncTrackedSurface` keeps `state.channel.registry` in lock-step with `state.activeRegistry`, so the cache still invalidates on legitimate registry swaps. No issues here, just noting the analysis for reviewers.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix(channels): use pinned channel regist..." | Re-trigger Greptile
|
@steipete @vincentkoc — this is a one-line fix for the root cause behind #54745 and #54013. TL;DR: The inbound path ( Changes:
All CI green, Greptile 5/5. |
2613fbb to
d2af02e
Compare
|
Rebased onto latest main — test file auto-merged cleanly despite the channel-pin test refactoring. Two-line fix: switches outbound adapter resolution from the unpinned active registry to the pinned channel registry, matching what inbound already does. One new test verifies outbound resolves from the pinned registry after an active registry replacement. @obviyus — this complements the registry reuse work you merged in #56240. The outbound path was the last consumer still reading from the unpinned surface. All checks green locally: 9/9 channel-pin tests, |
…ution loadChannelOutboundAdapter (via createChannelRegistryLoader) was reading from getActivePluginRegistry() — the unpinned active registry that gets replaced whenever loadOpenClawPlugins() runs (config schema reads, plugin status queries, tool listings, etc.). After replacement, the active registry may omit channel entries or carry them in setup mode without outbound adapters, causing: Outbound not configured for channel: telegram The channel inbound path already uses the pinned registry (getActivePluginChannelRegistry) which is frozen at gateway startup and survives all subsequent registry replacements. This commit aligns the outbound path to use the same pinned surface. Adds a regression test that pins a registry with a telegram outbound adapter, replaces the active registry with an empty one, then asserts loadChannelOutboundAdapter still resolves the adapter. Fixes openclaw#54745 Fixes openclaw#54013
d2af02e to
16bcd77
Compare
obviyus
left a comment
There was a problem hiding this comment.
Reviewed latest changes; landing now.
Summary
loadChannelOutboundAdapter(viacreateChannelRegistryLoaderinsrc/channels/plugins/registry-loader.ts) was reading fromgetActivePluginRegistry()— the unpinned active registry that gets replaced wheneverloadOpenClawPlugins()runs.Multiple code paths replace the active registry after boot:
runtime-schema.ts)plugins/status.ts)plugins/tools.ts)providers.runtime.ts)After replacement, the active registry may omit channel entries or carry them in setup mode without outbound adapters, causing:
The channel inbound path already uses the pinned registry (
getActivePluginChannelRegistry()viarequireActivePluginChannelRegistryinchannels/plugins/registry.ts), which is frozen at gateway startup and survives all subsequentsetActivePluginRegistrycalls. The outbound path was the only consumer still reading from the unpinned surface.Fix
Two-line change in
registry-loader.ts: switch fromgetActivePluginRegistry()togetActivePluginChannelRegistry(). This aligns outbound adapter resolution with the same pinned channel registry that inbound already uses.Registry divergence diagram
Test plan
loadChannelOutboundAdapterstill resolves the adapterpnpm tsgo— zero type errorspnpm lint(oxlint) — zero warnings/errorsFixes #54745
Fixes #54013
Related: #50721