fix(#2791): await session.abort() in all subagent completion/cancel paths
Fire-and-forget session.abort() calls during subagent completion left dangling promises that raced with parent session teardown. In Bun on WSL2/Linux, this triggered a StringImplShape assertion (SIGABRT) as WebKit GC collected string data still referenced by the inflight request. Fix: await session.abort() in all four completion/error paths: - startTask promptAsync error handler (launch path) - resume promptAsync error handler (resume path) - cancelTask (explicit cancel path) - tryCompleteTask (normal completion path) Also marks the two .catch() error callbacks as async so the await is valid. Test: update session.deleted cascade test to flush two microtask rounds since cancelTask now awaits abort before cleanupPendingByParent.
This commit is contained in:
@@ -3728,6 +3728,9 @@ describe("BackgroundManager.handleEvent - session.deleted cascade", () => {
|
||||
properties: { info: { id: parentSessionID } },
|
||||
})
|
||||
|
||||
// Flush twice: cancelTask now awaits session.abort() before cleanupPendingByParent,
|
||||
// so we need additional microtask ticks to let the cascade complete fully.
|
||||
await flushBackgroundNotifications()
|
||||
await flushBackgroundNotifications()
|
||||
|
||||
// then
|
||||
|
||||
@@ -538,7 +538,7 @@ export class BackgroundManager {
|
||||
})(),
|
||||
parts: [createInternalAgentTextPart(input.prompt)],
|
||||
},
|
||||
}).catch((error) => {
|
||||
}).catch(async (error) => {
|
||||
log("[background-agent] promptAsync error:", error)
|
||||
const existingTask = this.findBySession(sessionID)
|
||||
if (existingTask) {
|
||||
@@ -561,7 +561,8 @@ export class BackgroundManager {
|
||||
removeTaskToastTracking(existingTask.id)
|
||||
|
||||
// Abort the session to prevent infinite polling hang
|
||||
this.client.session.abort({
|
||||
// Awaited to prevent dangling promise during subagent teardown (Bun/WebKit SIGABRT)
|
||||
await this.client.session.abort({
|
||||
path: { id: sessionID },
|
||||
}).catch(() => {})
|
||||
|
||||
@@ -823,7 +824,7 @@ export class BackgroundManager {
|
||||
})(),
|
||||
parts: [createInternalAgentTextPart(input.prompt)],
|
||||
},
|
||||
}).catch((error) => {
|
||||
}).catch(async (error) => {
|
||||
log("[background-agent] resume prompt error:", error)
|
||||
existingTask.status = "interrupt"
|
||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||
@@ -842,8 +843,9 @@ export class BackgroundManager {
|
||||
removeTaskToastTracking(existingTask.id)
|
||||
|
||||
// Abort the session to prevent infinite polling hang
|
||||
// Awaited to prevent dangling promise during subagent teardown (Bun/WebKit SIGABRT)
|
||||
if (existingTask.sessionID) {
|
||||
this.client.session.abort({
|
||||
await this.client.session.abort({
|
||||
path: { id: existingTask.sessionID },
|
||||
}).catch(() => {})
|
||||
}
|
||||
@@ -1392,7 +1394,8 @@ export class BackgroundManager {
|
||||
}
|
||||
|
||||
if (abortSession && task.sessionID) {
|
||||
this.client.session.abort({
|
||||
// Awaited to prevent dangling promise during subagent teardown (Bun/WebKit SIGABRT)
|
||||
await this.client.session.abort({
|
||||
path: { id: task.sessionID },
|
||||
}).catch(() => {})
|
||||
|
||||
@@ -1510,7 +1513,8 @@ export class BackgroundManager {
|
||||
}
|
||||
|
||||
if (task.sessionID) {
|
||||
this.client.session.abort({
|
||||
// Awaited to prevent dangling promise during subagent teardown (Bun/WebKit SIGABRT)
|
||||
await this.client.session.abort({
|
||||
path: { id: task.sessionID },
|
||||
}).catch(() => {})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user