From ec263d62b6e3bfd7df6e5a256d1148b0b277bc1e Mon Sep 17 00:00:00 2001 From: Filip <34747899+neriousy@users.noreply.github.com> Date: Thu, 12 Nov 2026 16:15:41 +0100 Subject: [PATCH] fix(core): keep plugin swaps going when a cleanup fails (#63548) Co-authored-by: Filip Hejmowski --- packages/core/src/plugin.ts | 10 ++++++++-- packages/plugin/src/promise/adapter.ts | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/core/src/plugin.ts b/packages/core/src/plugin.ts index ca3175c96a9..d6b11356ed1 100644 --- a/packages/core/src/plugin.ts +++ b/packages/core/src/plugin.ts @@ -143,8 +143,14 @@ const layer = Layer.effect( ([id, slot]) => Effect.gen(function* () { active.delete(id) - if (slot.activation && !slot.activation.failure) - yield* Scope.close(slot.activation.scope, Exit.void) + if (!slot.activation || slot.activation.failure) return + // Later plugins, including the config agents and commands, are already closed. A failing + // finalizer must not abort the swap, or they stay unregistered until the next reload. + yield* Scope.close(slot.activation.scope, Exit.void).pipe( + Effect.catchCause((cause) => + Effect.logError("failed to clean up plugin", { "plugin.id": id, cause }), + ), + ) }), { discard: true }, ) diff --git a/packages/plugin/src/promise/adapter.ts b/packages/plugin/src/promise/adapter.ts index ba81e6ee224..ec91156ba46 100644 --- a/packages/plugin/src/promise/adapter.ts +++ b/packages/plugin/src/promise/adapter.ts @@ -672,7 +672,8 @@ export function fromPromise(plugin: Plugin) { yield* Effect.acquireRelease( Effect.promise(() => Promise.resolve(plugin.setup(context2))), - (cleanup) => (cleanup ? Effect.promise(() => Promise.resolve(cleanup())) : Effect.void), + // JavaScript plugins may return anything, such as a v1-style hooks object; only a function is a cleanup. + (cleanup) => (typeof cleanup === "function" ? Effect.promise(() => Promise.resolve(cleanup())) : Effect.void), ) }), })