{"record":{"id":"0c1fa0e6bdf2cfef","repo":"moeru-ai/airi","slug":"text-reading-aborted","errorCode":null,"errorMessage":"Text reading aborted","messagePattern":"Text reading aborted","errorType":"console","errorClass":null,"httpStatus":null,"severity":"info","filePath":"packages/stage-ui/src/components/widgets/poppin-text/PoppinText.web.vue","lineNumber":102,"sourceCode":"  abortController.value = new AbortController()\n\n  try {\n    streamTextGeneration += 1\n    animatedTargetIds.clear()\n    targets.value = []\n\n    for await (const cluster of readGraphemeClusters(text.getReader(), { signal: abortController.value.signal })) {\n      targets.value.push({\n        id: `stream:${streamTextGeneration}:${targets.value.length}`,\n        grapheme: cluster,\n      })\n\n      emits('textSplit', cluster)\n    }\n  }\n  catch (error) {\n    if (error instanceof Error && error.message === 'Aborted') {\n      console.warn('Text reading aborted')\n    }\n    else {\n      console.error('Error reading text:', error)\n    }\n  }\n}, { immediate: true })\n\nconst elements = ref<HTMLElement[]>([])\nconst animatorCleanupFn = shallowRef<() => void>()\nconst activeAnimator = shallowRef<Animator>()\n\nonMounted(() => {\n  animatorCleanupFn.value = props.animator?.(elements.value.slice())\n  activeAnimator.value = props.animator\n  targets.value.forEach(target => animatedTargetIds.add(target.id))\n})\n\nwatch([targets, () => props.animator], ([targets, animator]) => {","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-ui/src/components/widgets/poppin-text/PoppinText.web.vue#L84-L120","documentation":"PoppinText.web.vue streams incoming text grapheme-by-grapheme via readGraphemeClusters(text.getReader(), { signal }). When the effect re-runs (new text) or the component tears down, the AbortController aborts and the stream read rejects with Error('Aborted'). The catch recognizes that message and logs this warn: it is the designed cancellation path, not an error — real failures go to console.error in the else branch.","triggerScenarios":"props.text changes while a previous stream is still being read (rapid LLM token updates), component unmount mid-stream, or any explicit abortController.abort() before the reader finished.","commonSituations":"Fast consecutive streaming updates replacing each other; navigating away while text animates; a new generation cancelling the previous one.","solutions":["No code fix needed — this is expected cancellation; exclude the known 'Aborted' message from error telemetry","If the warning is noisy, downgrade it to console.debug or drop it entirely since the abort was requested by this component itself","Verify genuine failures are not masked: keep the else branch reporting unexpected errors"],"exampleFix":"// before\nif (error instanceof Error && error.message === 'Aborted') {\n  console.warn('Text reading aborted')\n}\nelse {\n  console.error('Error reading text:', error)\n}\n\n// after\nif (error instanceof Error && error.message === 'Aborted') {\n  // expected: we aborted the previous read ourselves\n  return\n}\nconsole.error('Error reading text:', error)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isAbortError(error: unknown): boolean {\n  return error instanceof Error && (error.message === 'Aborted' || error.name === 'AbortError')\n}","tryCatchPattern":"catch (error) {\n  if (isAbortError(error)) {\n    return // self-requested cancellation, not an error\n  }\n  console.error('Error reading text:', error)\n}","preventionTips":["Recognize your own aborts by message/name before logging them as warnings","Check abortController.value.signal.aborted first and skip the loop entirely","Keep a distinct console.error branch so genuine read failures stay visible"],"tags":["abort-controller","streaming","text-reading","vue-watch"],"backgroundTag":"abort-signal-cancelled","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}