{"record":{"id":"9e8f402b12987e2f","repo":"can1357/oh-my-pi","slug":"ask-input-was-cancelled","errorCode":null,"errorMessage":"Ask input was cancelled","messagePattern":"Ask input was cancelled","errorType":"exception","errorClass":"ToolAbortError","httpStatus":null,"severity":"info","filePath":"packages/coding-agent/src/tools/ask.ts","lineNumber":984,"sourceCode":"\t\t\t\t\t}\n\t\t\t\t\tconst details: AskToolDetails = {\n\t\t\t\t\t\tquestion: result.question,\n\t\t\t\t\t\toptions: result.options,\n\t\t\t\t\t\tmulti: result.multi,\n\t\t\t\t\t\tselectedOptions: result.selectedOptions,\n\t\t\t\t\t\tcustomInput: result.customInput,\n\t\t\t\t\t\tnote: result.note,\n\t\t\t\t\t\ttimedOut: result.timedOut,\n\t\t\t\t\t};\n\t\t\t\t\tconst responseText = formatSingleQuestionResponse(result);\n\t\t\t\t\treturn { content: [{ type: \"text\" as const, text: responseText }], details };\n\t\t\t\t}\n\t\t\t\tconst details: AskToolDetails = { results };\n\t\t\t\tconst responseText = `User answers:\\n${results.map(formatQuestionResult).join(\"\\n\")}`;\n\t\t\t\treturn { content: [{ type: \"text\" as const, text: responseText }], details };\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof Error && error.name === \"AbortError\") {\n\t\t\t\t\tthrow new ToolAbortError(\"Ask input was cancelled\");\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tconst askQuestion = async (\n\t\t\tq: AskParams[\"questions\"][number],\n\t\t\toptions?: { previous?: QuestionResult; navigation?: NavigationControls },\n\t\t) => {\n\t\t\tconst questionOptions = q.options.map(option => ({\n\t\t\t\tlabel: option.label,\n\t\t\t\t...(option.description?.trim() ? { description: option.description.trim() } : {}),\n\t\t\t}));\n\t\t\tconst optionLabels = questionOptions.map(getAskOptionLabel);\n\t\t\ttry {\n\t\t\t\tconst { selectedOptions, customInput, note, navigation, cancelled, timedOut } = await askSingleQuestion(\n\t\t\t\t\tui,\n\t\t\t\t\tq.question,","sourceCodeStart":966,"sourceCodeEnd":1002,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/ask.ts#L966-L1002","documentation":"After collecting answers for all questions, the ask tool wraps its await in a catch that converts a DOM-style AbortError (name === 'AbortError') into ToolAbortError('Ask input was cancelled'). This covers the multi-question flow's aggregated prompt being aborted while waiting for input. It rethrows all other errors unchanged.","triggerScenarios":"An AbortError is raised by the underlying prompt/UI call while awaiting the user's answers (e.g. context.abort() from another path, or signal abort from the agent run being cancelled mid-prompt).","commonSituations":"The agent session is aborted externally while a multi-question ask dialog is open; the user cancels via a UI control that surfaces as a plain AbortError rather than the tool's own cancelled flag.","solutions":["Catch ToolAbortError at the tool-call boundary and treat it as a graceful stop.","Check why the AbortSignal fired if cancellation was unexpected (upstream context.abort(), timeout, or session teardown).","Ensure callers pass a live signal and don't abort the context while the ask prompt is legitimately open."],"exampleFix":"// before\nconst result = await askTool.execute(params, signal);\n// after\nlet result;\ntry {\n  result = await askTool.execute(params, signal);\n} catch (err) {\n  if (err instanceof ToolAbortError && /Ask input was cancelled/.test(err.message)) {\n    return { cancelled: true };\n  }\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":"// before calling, ensure the signal is not already aborted\nif (signal.aborted) return { status: 'cancelled' };","typeGuard":"function isAbortNamedError(e: unknown): e is Error {\n  return e instanceof Error && e.name === 'AbortError';\n}","tryCatchPattern":"try {\n  result = await askTool.execute(params, signal, onUpdate, context);\n} catch (err) {\n  if (err instanceof ToolAbortError) return { status: 'cancelled' };\n  throw err;\n}","preventionTips":["Don't abort the context while an ask prompt is intentionally open.","Wire a single cancellation path so UI cancel maps to ToolAbortError, not a raw AbortError.","Check upstream abort sources (timeouts, session teardown) if cancels appear spontaneously."],"tags":["abort","user-cancellation","tool-abort"],"backgroundTag":"user-cancelled-tool","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}