moeru-ai/airi · info
Text reading aborted
Error message
Text reading aborted
What it means
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.
Source
Thrown at packages/stage-ui/src/components/widgets/poppin-text/PoppinText.web.vue:102
abortController.value = new AbortController()
try {
streamTextGeneration += 1
animatedTargetIds.clear()
targets.value = []
for await (const cluster of readGraphemeClusters(text.getReader(), { signal: abortController.value.signal })) {
targets.value.push({
id: `stream:${streamTextGeneration}:${targets.value.length}`,
grapheme: cluster,
})
emits('textSplit', cluster)
}
}
catch (error) {
if (error instanceof Error && error.message === 'Aborted') {
console.warn('Text reading aborted')
}
else {
console.error('Error reading text:', error)
}
}
}, { immediate: true })
const elements = ref<HTMLElement[]>([])
const animatorCleanupFn = shallowRef<() => void>()
const activeAnimator = shallowRef<Animator>()
onMounted(() => {
animatorCleanupFn.value = props.animator?.(elements.value.slice())
activeAnimator.value = props.animator
targets.value.forEach(target => animatedTargetIds.add(target.id))
})
watch([targets, () => props.animator], ([targets, animator]) => {View on GitHub (pinned to 677329427f)
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
Example fix
// before
if (error instanceof Error && error.message === 'Aborted') {
console.warn('Text reading aborted')
}
else {
console.error('Error reading text:', error)
}
// after
if (error instanceof Error && error.message === 'Aborted') {
// expected: we aborted the previous read ourselves
return
}
console.error('Error reading text:', error) Defensive patterns
Strategy: try-catch
Type guard
function isAbortError(error: unknown): boolean {
return error instanceof Error && (error.message === 'Aborted' || error.name === 'AbortError')
} Try / catch
catch (error) {
if (isAbortError(error)) {
return // self-requested cancellation, not an error
}
console.error('Error reading text:', error)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Fast consecutive streaming updates replacing each other; navigating away while text animates; a new generation cancelling the previous one.
Related errors
- OpenRouter audio response has no body
- Skipping malformed SSE chunk from OpenRouter audio stream:
- streaming models upstream missing models[]
- [llm-streaming-control] signal handler failed
- [llm-streaming-control] handler failed
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/0c1fa0e6bdf2cfef.
Report an issue: GitHub.