{"record":{"id":"1059fb8f551ed41f","repo":"earendil-works/pi","slug":"operation-aborted-1059fb","errorCode":null,"errorMessage":"Operation aborted","messagePattern":"Operation aborted","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"packages/agent/src/harness/tools/write.ts","lineNumber":29,"sourceCode":"});\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\nexport function createWriteTool<TContext extends ExecutionToolContext = ExecutionToolContext>(): AgentHarnessTool<\n\tTContext,\n\ttypeof writeSchema,\n\tundefined\n> {\n\treturn {\n\t\tname: \"write\",\n\t\tlabel: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tparameters: writeSchema,\n\t\tasync execute(_toolCallId, { path, content }, signal, _onUpdate, { env }) {\n\t\t\tconst absolutePath = await resolveToolPath(env, path, signal);\n\t\t\treturn withFileMutationQueue(env, absolutePath, async () => {\n\t\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\t\tgetOrThrow(await env.writeFile(absolutePath, content, signal));\n\t\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Successfully wrote ${content.length} bytes to ${path}` }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t};\n}\n","sourceCodeStart":11,"sourceCodeEnd":40,"githubUrl":"https://github.com/earendil-works/pi/blob/4af9d21d3b4d664e4a29fcabfec85171077248e3/packages/agent/src/harness/tools/write.ts#L11-L40","documentation":"The write harness tool checks its AbortSignal inside the per-file mutation queue and throws this when cancellation was requested before env.writeFile runs (write.ts:29). It is cooperative cancellation, not a fault: the tool deliberately skips the write. The target file is still unmodified at this checkpoint.","triggerScenarios":"Calling the write tool's execute() with a signal whose AbortController fires during resolveToolPath or while entering withFileMutationQueue - user pressed stop, a timeout controller expired, or a shared batch signal was aborted by a sibling failure.","commonSituations":"Stop buttons wired to the tool-execution signal; request-scoped controllers aborted on component unmount; fail-fast parallel tool batches sharing one signal.","solutions":["Treat it as cancellation: check signal.aborted in the catch block and record a skipped/cancelled tool result","Abort between tool calls instead of mid-call if you need all-or-nothing behavior","Check that no unrelated AbortController (timeout, unmount, sibling failure) is aborting the shared signal"],"exampleFix":"// before\nconst result = await writeTool.execute(id, input, signal, undefined, ctx);\n\n// after - treat abort as cancellation\ntry {\n  const result = await writeTool.execute(id, input, signal, undefined, ctx);\n} catch (e) {\n  if (signal?.aborted) return { content: [{ type: \"text\", text: \"Write cancelled\" }], details: undefined };\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"if (signal?.aborted) {\n  return { content: [{ type: \"text\", text: \"Write skipped: request already cancelled\" }], details: undefined };\n}\nawait writeTool.execute(id, input, signal, undefined, ctx);","typeGuard":"const isAbortError = (e: unknown): boolean =>\n  e instanceof Error && e.message === \"Operation aborted\";","tryCatchPattern":"try {\n  return await writeTool.execute(id, input, signal, undefined, ctx);\n} catch (e) {\n  if (signal?.aborted && e instanceof Error && e.message === \"Operation aborted\") {\n    return cancelledResult(); // file untouched at this checkpoint\n  }\n  throw e;\n}","preventionTips":["Check signal.aborted before invoking the tool","Abort between tool calls for all-or-nothing semantics","Keep AbortController ownership explicit and single-purpose"],"tags":["abort","cancellation","abortsignal","write-tool","filesystem"],"backgroundTag":"abortsignal-cancellation","analyzedSha":"4af9d21d3b4d664e4a29fcabfec85171077248e3","analyzedAt":"2026-08-24T13:07:14.692Z","schemaVersion":2},"datasetVersion":"2026-08-24T17:17:21.512Z"}