{"record":{"id":"f1a6ae6b2d2fd459","repo":"can1357/oh-my-pi","slug":"aborted-before-execution-f1a6ae","errorCode":null,"errorMessage":"Aborted before execution","messagePattern":"Aborted before execution","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"info","filePath":"packages/coding-agent/src/task/index.ts","lineNumber":1129,"sourceCode":"\t\t\t\t\tif (!semaphoreHeld) return;\n\t\t\t\t\tsemaphoreHeld = false;\n\t\t\t\t\tthis.#releaseSpawnSemaphore();\n\t\t\t\t};\n\t\t\t\ttry {\n\t\t\t\t\tawait semaphore.acquire(runSignal);\n\t\t\t\t\tsemaphoreHeld = true;\n\t\t\t\t} catch {\n\t\t\t\t\t// Fall through so an acquire-time abort goes through the same\n\t\t\t\t\t// path as the post-acquire race below: progress + onSettled\n\t\t\t\t\t// have to fire even when the spawn never reached the executor,\n\t\t\t\t\t// otherwise the batch aggregate state stays \"running\" forever.\n\t\t\t\t}\n\t\t\t\tconst acquiredAt = Date.now();\n\t\t\t\tif (!semaphoreHeld || runSignal.aborted) {\n\t\t\t\t\treleasePermit();\n\t\t\t\t\tprogress.status = \"aborted\";\n\t\t\t\t\tonSettled?.(true);\n\t\t\t\t\tthrow new Error(\"Aborted before execution\");\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tmarkRunning();\n\t\t\t\t\tprogress.status = \"running\";\n\t\t\t\t\tawait reportProgress(\n\t\t\t\t\t\t`Running background task ${agentId}...`,\n\t\t\t\t\t\tbuildDetails() as unknown as Record<string, unknown>,\n\t\t\t\t\t);\n\t\t\t\t\tconst forwardSyncProgress: AgentToolUpdateCallback<TaskToolDetails> = async update => {\n\t\t\t\t\t\tconst nextProgress = update.details?.progress?.[0];\n\t\t\t\t\t\tif (nextProgress) {\n\t\t\t\t\t\t\t// The job body owns status and identity (id/index/agent);\n\t\t\t\t\t\t\t// copy only the live metrics the subagent streams so the\n\t\t\t\t\t\t\t// polling row reflects the resolved model, reasoning level,\n\t\t\t\t\t\t\t// and running counters without reverting the \"running\"\n\t\t\t\t\t\t\t// status back to the subagent's initial \"pending\" snapshot.\n\t\t\t\t\t\t\tprogress.modelRole = nextProgress.modelRole ?? progress.modelRole;\n\t\t\t\t\t\t\tprogress.resolvedModel = nextProgress.resolvedModel;","sourceCodeStart":1111,"sourceCodeEnd":1147,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/task/index.ts#L1111-L1147","documentation":"The Task tool's registered background job waits on the spawn semaphore, then re-checks the abort signal before executing. If the semaphore was never acquired (acquire threw due to abort) or the run signal aborted during/after acquisition, the job marks progress aborted, fires onSettled(true), and throws 'Aborted before execution' — the task never starts.","triggerScenarios":"TaskTool background spawn where the session/signal is aborted while the job is queued waiting for a concurrency permit, or aborted in the window between semaphore.acquire() resolving and execution beginning.","commonSituations":"User presses Esc/cancels while several background tasks are queued behind task.maxConcurrency, an ESC-interrupted agent turn aborting its child Task calls, or a timeout aborting the parent signal.","solutions":["No fix needed if intentional cancellation — this is the expected abort path; handle/suppress the abort in the caller.","Increase task.maxConcurrency if tasks are routinely aborted while queued too long (if the abort stems from a timeout).","Re-issue the task after the session is un-aborted; the agent remains resumable per the follow-up hint."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// check before spawning a background task\nif (signal?.aborted) {\n  console.log(\"skip: session already aborted\");\n} else if (pendingTasks >= maxConcurrency) {\n  console.log(\"task will queue; abort during queueing throws 'Aborted before execution'\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  await taskTool.run(spawnParams);\n} catch (err) {\n  if (err instanceof Error && err.message === \"Aborted before execution\") {\n    // intentional cancellation: update batch state, do not surface as failure\n    return { aborted: true };\n  }\n  throw err;\n}","preventionTips":["Don't abort the parent signal while background tasks are still queued, or treat this outcome as expected.","Size task.maxConcurrency so queued waits stay short.","Check progress.status === \"aborted\" to distinguish cancellation from real failures."],"tags":["abort","cancellation","concurrency","task"],"backgroundTag":"operation-aborted","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}