{"record":{"id":"4601826e6fb3f152","repo":"can1357/oh-my-pi","slug":"toolaborterror-operation-aborted","errorCode":null,"errorMessage":"ToolAbortError (operation aborted)","messagePattern":"ToolAbortError \\(operation aborted\\)","errorType":"exception","errorClass":"ToolAbortError","httpStatus":null,"severity":"info","filePath":"packages/coding-agent/src/tools/path-utils.ts","lineNumber":1398,"sourceCode":"\tsignal: AbortSignal | undefined,\n\tglobImpl: typeof glob,\n): Promise<{ absolutePath: string; displayPath: string } | null> {\n\tconst normalized = rawPath.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\").replace(/\\/+$/, \"\");\n\tif (!normalized) return null;\n\n\tconst timeoutSignal = AbortSignal.timeout(WORKSPACE_SUFFIX_TIMEOUT_MS);\n\tconst combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;\n\n\tlet matches: string[];\n\ttry {\n\t\tconst result = await globImpl({\n\t\t\tpattern: `**/${escapeGlobMetachars(normalized)}`,\n\t\t\tpath: cwd,\n\t\t\thidden: true,\n\t\t\tsignal: combinedSignal,\n\t\t\ttimeoutMs: WORKSPACE_SUFFIX_TIMEOUT_MS,\n\t\t});\n\t\tif (signal?.aborted) throw new ToolAbortError();\n\t\tmatches = result.matches.map(match => match.path);\n\t} catch {\n\t\tif (signal?.aborted) throw new ToolAbortError();\n\t\treturn null;\n\t}\n\n\tif (matches.length !== 1) return null;\n\treturn {\n\t\tabsolutePath: path.resolve(cwd, matches[0]),\n\t\tdisplayPath: matches[0],\n\t};\n}\n\n/**\n * Find a unique workspace entry whose trailing path matches a missing authored path.\n * Returns `null` for no match, ambiguity, timeout, or scan failure.\n */\nexport async function findUniqueWorkspaceSuffix(","sourceCodeStart":1380,"sourceCodeEnd":1416,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/path-utils.ts#L1380-L1416","documentation":"findUniqueWorkspaceSuffixWithGlob throws ToolAbortError immediately after the glob scan returns when the caller-supplied AbortSignal is aborted. This converts the caller's cancellation into the tool layer's standard abort error, since the native glob may complete without observing the signal.","triggerScenarios":"The AbortSignal passed to findUniqueWorkspaceSuffix becomes aborted while (or just after) the `**/<suffix>` glob scan runs, and the glob call itself returns normally — the post-call `signal?.aborted` check at path-utils.ts:1398 fires.","commonSituations":"User presses Escape / cancels the tool call while the workspace suffix lookup is scanning a large tree; upstream request times out and aborts the shared signal.","solutions":["No fix needed — this is expected cancellation propagation; treat it as the operation being cancelled.","If it fires unexpectedly, audit who owns the AbortSignal and whether cancellation should still be active at this point.","If scans are routinely cancelled mid-flight, reduce scan scope or skip the suffix heuristic for very large workspaces."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const hit = await findUniqueWorkspaceSuffix(p, cwd, signal);\n} catch (e) {\n  if (e instanceof ToolAbortError || signal?.aborted) return; // cancelled: stop quietly\n  throw e;\n}","preventionTips":["Propagate a single AbortSignal per tool invocation and check `signal.aborted` after each await.","Treat ToolAbortError as normal cancellation in top-level tool handlers.","Avoid sharing signals across unrelated operations."],"tags":["abort","cancellation","glob","async"],"backgroundTag":"operation-aborted","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}