diff --git a/src/gateway/server-runtime-state.ts b/src/gateway/server-runtime-state.ts index a2ab6ce0fc9..47969f821f9 100644 --- a/src/gateway/server-runtime-state.ts +++ b/src/gateway/server-runtime-state.ts @@ -236,7 +236,11 @@ export async function createGatewayRuntimeState(params: { releasePluginRouteRegistry: () => { // Releases both pinned HTTP-route and channel registries set at startup. releasePinnedPluginHttpRouteRegistry(params.pluginRegistry); - releasePinnedPluginChannelRegistry(params.pluginRegistry); + // Release unconditionally (no registry arg): the channel pin may have + // been re-pinned to a deferred-reload registry that differs from the + // original params.pluginRegistry, so an identity-guarded release would + // be a no-op and leak the pin across in-process restarts. + releasePinnedPluginChannelRegistry(); }, httpServer, httpServers, @@ -258,7 +262,7 @@ export async function createGatewayRuntimeState(params: { }; } catch (err) { releasePinnedPluginHttpRouteRegistry(params.pluginRegistry); - releasePinnedPluginChannelRegistry(params.pluginRegistry); + releasePinnedPluginChannelRegistry(); throw err; } } diff --git a/src/gateway/server.impl.ts b/src/gateway/server.impl.ts index b1e249271b7..4d225bcbcaf 100644 --- a/src/gateway/server.impl.ts +++ b/src/gateway/server.impl.ts @@ -53,6 +53,7 @@ import { createSubsystemLogger, runtimeForLogger } from "../logging/subsystem.js import { resolveConfiguredDeferredChannelPluginIds } from "../plugins/channel-plugin-ids.js"; import { getGlobalHookRunner, runGlobalGatewayStopSafely } from "../plugins/hook-runner-global.js"; import { createEmptyPluginRegistry } from "../plugins/registry.js"; +import { pinActivePluginChannelRegistry } from "../plugins/runtime.js"; import { createPluginRuntime } from "../plugins/runtime/index.js"; import type { PluginServicesHandle } from "../plugins/services.js"; import { getTotalQueueSize } from "../process/command-queue.js"; @@ -1195,6 +1196,10 @@ export async function startGatewayServer( baseMethods, logDiagnostics: false, })); + // Re-pin: the deferred reload replaces setup-entry channel objects with + // full runtime implementations. Update the pinned channel registry so + // getChannelPlugin() resolves against the complete set. + pinActivePluginChannelRegistry(pluginRegistry); } ({ browserControl, pluginServices } = await startGatewaySidecars({ cfg: cfgAtStart,