{"record":{"id":"0676cdff3a310c17","repo":"vercel/ai","slug":"stream-aborted","errorCode":null,"errorMessage":"Stream aborted","messagePattern":"Stream aborted","errorType":"exception","errorClass":"DOMException","httpStatus":null,"severity":"info","filePath":"packages/angular/src/lib/structured-object.ng.ts","lineNumber":177,"sourceCode":"\n      if (!response.ok) {\n        throw new Error(\n          (await response.text()) ?? 'Failed to fetch the response.',\n        );\n      }\n\n      if (response.body == null) {\n        throw new Error('The response body is empty.');\n      }\n\n      let accumulatedText = '';\n      let latestObject: DeepPartial<RESULT> | undefined = undefined;\n\n      await response.body.pipeThrough(new TextDecoderStream()).pipeTo(\n        new WritableStream<string>({\n          write: async chunk => {\n            if (abortController?.signal.aborted) {\n              throw new DOMException('Stream aborted', 'AbortError');\n            }\n            accumulatedText += chunk;\n\n            const { value } = await parsePartialJson(accumulatedText);\n            const currentObject = value as DeepPartial<RESULT>;\n\n            if (!isDeepEqualData(latestObject, currentObject)) {\n              latestObject = currentObject;\n\n              this.#object.set(currentObject);\n            }\n          },\n\n          close: async () => {\n            this.#loading.set(false);\n            this.#abortController = undefined;\n\n            if (this.options.onFinish != null) {","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/angular/src/lib/structured-object.ng.ts#L159-L195","documentation":"StructuredObject aborts its underlying stream when the consumer calls `stop()`. The write handler checks `abortController.signal.aborted` on each chunk and throws an `AbortError` DOMException named 'Stream aborted' to unwind the pipe. It is an intentional cancellation signal, not a malfunction.","triggerScenarios":"Calling the object instance's `stop()` method while the response stream is actively piping chunks, so the next `write()` in the WritableStream sees an aborted signal and throws.","commonSituations":"Component unmount/cleanup (NgOnDestroy) calling `stop()` mid-stream; user pressing a cancel button; route navigation while an object is still generating.","solutions":["Treat this as expected control flow: filter out/catch AbortError in your error handler for the object's error stream.","Call `stop()` only when you intend to cancel, and stop listening for further updates.","In cleanup code, guard so unmount-time aborts don't surface as user-facing errors."],"exampleFix":"// before\nobject.error.subscribe(err => console.error(err));\n// after\nobject.error.subscribe(err => {\n  if (err instanceof DOMException && err.name === 'AbortError') return;\n  console.error(err);\n});","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"function isAbortError(e: unknown): e is DOMException {\n  return e instanceof DOMException && e.name === 'AbortError';\n}","tryCatchPattern":"try {\n  await streamPipe;\n} catch (e) {\n  if (isAbortError(e)) return; // expected after stop()\n  throw e;\n}","preventionTips":["Always catch/swallow AbortError after intentional stop().","Abort in ngOnDestroy/cleanup and stop subscribing afterwards.","Treat 'Stream aborted' as control flow, not a failure to report to users."],"tags":["abort","streaming","angular","cancellation"],"backgroundTag":"stream-aborted","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}