From 74ab021195144c0fe21e5bff729204401e3a8e52 Mon Sep 17 00:00:00 2001 From: Romil Bijarnia Date: Wed, 11 Nov 2026 17:22:10 +1100 Subject: [PATCH] fix(server): detach errored provider sessions when deleting threads --- apps/server/src/orchestration-v2/ThreadDeletion.test.ts | 8 +++++++- apps/server/src/orchestration-v2/ThreadDeletion.ts | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/server/src/orchestration-v2/ThreadDeletion.test.ts b/apps/server/src/orchestration-v2/ThreadDeletion.test.ts index ef7c23bf75120..a9f20246eb000 100644 --- a/apps/server/src/orchestration-v2/ThreadDeletion.test.ts +++ b/apps/server/src/orchestration-v2/ThreadDeletion.test.ts @@ -279,7 +279,7 @@ it.effect("queues provider and resource cleanup and preserves an earlier deletio assert.deepEqual(deleted.thread.deletedAt, createdAt); assert.deepEqual( deleted.providerSessions.map((session) => session.status), - ["stopped", "error"], + ["stopped"], ); assert.deepEqual( plan.effects.map((effect) => effect.request), @@ -290,6 +290,12 @@ it.effect("queues provider and resource cleanup and preserves an earlier deletio detail: "Thread deleted.", revokeMcpCredential: true, }, + { + type: "provider-session.detach", + providerSessionId: ProviderSessionId.make("session:delete-plan:error"), + detail: "Thread deleted.", + revokeMcpCredential: true, + }, { type: "scheduled-tasks.cleanup" }, { type: "terminal.cleanup" }, { type: "preview.cleanup" }, diff --git a/apps/server/src/orchestration-v2/ThreadDeletion.ts b/apps/server/src/orchestration-v2/ThreadDeletion.ts index 67a27fcf76962..265f59008d87f 100644 --- a/apps/server/src/orchestration-v2/ThreadDeletion.ts +++ b/apps/server/src/orchestration-v2/ThreadDeletion.ts @@ -183,7 +183,8 @@ export const planThreadDeletion = Effect.fn("ThreadDeletion.planThreadDeletion") } for (const session of projection.providerSessions) { - if (session.status === "stopped" || session.status === "error") continue; + // An errored session can still own a reusable live runtime. + if (session.status === "stopped") continue; yield* emitEvent({ type: "provider-session.detached", threadId: command.threadId,