fix(background-agent): prevent concurrency slot leaks on task startup failures

Unify slot ownership: processKey() owns the slot until task.concurrencyKey is set.
Previously, startTask() released the slot on errors (session.create catch,
createResult.error), but processKey() catch block didn't release, causing slot
leaks when errors occurred between acquire() and task.concurrencyKey assignment.

Changes:
- Remove all pre-transfer release() calls in startTask()
- Add conditional release in processKey() catch: only if task.concurrencyKey not set
- Add validation for createResult.data?.id to catch malformed API responses

This fixes 'Task failed to start within timeout' errors caused by exhausted
concurrency slots that were never released.
This commit is contained in:
YeonGyu-Kim
2026-02-01 21:24:52 +09:00
parent 613610308c
commit 25dcd2a3f2

View File

@@ -200,6 +200,11 @@ export class BackgroundManager {
await this.startTask(item)
} catch (error) {
log("[background-agent] Error starting task:", error)
// Release concurrency slot if startTask failed and didn't release it itself
// This prevents slot leaks when errors occur after acquire but before task.concurrencyKey is set
if (!item.task.concurrencyKey) {
this.concurrencyManager.release(key)
}
}
queue.shift()
@@ -240,16 +245,16 @@ export class BackgroundManager {
query: {
directory: parentDirectory,
},
}).catch((error) => {
this.concurrencyManager.release(concurrencyKey)
throw error
})
if (createResult.error) {
this.concurrencyManager.release(concurrencyKey)
throw new Error(`Failed to create background session: ${createResult.error}`)
}
if (!createResult.data?.id) {
throw new Error("Failed to create background session: API returned no session ID")
}
const sessionID = createResult.data.id
subagentSessions.add(sessionID)