diff --git a/packages/next/src/server/pipe-readable.ts b/packages/next/src/server/pipe-readable.ts index 1d9602b103e92d..d432af579e4e46 100644 --- a/packages/next/src/server/pipe-readable.ts +++ b/packages/next/src/server/pipe-readable.ts @@ -2,11 +2,14 @@ import type { ServerResponse } from 'node:http' import './node-polyfill-web-streams' -import { createAbortController } from './web/spec-extension/adapters/next-request' +import { + ResponseAbortedName, + createAbortController, +} from './web/spec-extension/adapters/next-request' import { DetachedPromise } from '../lib/detached-promise' export function isAbortError(e: any): e is Error & { name: 'AbortError' } { - return e?.name === 'AbortError' + return e?.name === 'AbortError' || e?.name === ResponseAbortedName } function createWriterFromResponse( @@ -66,7 +69,7 @@ function createWriterFromResponse( } } catch (err) { res.end() - throw err + throw new Error('failed to write chunk to response', { cause: err }) } }, abort: (err) => { @@ -101,8 +104,8 @@ export async function pipeToNodeResponse( await readable.pipeTo(writer, { signal: controller.signal }) } catch (err: any) { // If this isn't related to an abort error, re-throw it. - if (!isAbortError(err)) { - throw err - } + if (isAbortError(err)) return + + throw new Error('failed to pipe response', { cause: err }) } } diff --git a/packages/next/src/server/web/spec-extension/adapters/next-request.ts b/packages/next/src/server/web/spec-extension/adapters/next-request.ts index 72cee96609479c..c74a312c4a11a1 100644 --- a/packages/next/src/server/web/spec-extension/adapters/next-request.ts +++ b/packages/next/src/server/web/spec-extension/adapters/next-request.ts @@ -7,6 +7,11 @@ import { getRequestMeta } from '../../../request-meta' import { fromNodeOutgoingHttpHeaders } from '../../utils' import { NextRequest } from '../request' +export const ResponseAbortedName = 'ResponseAborted' +export class ResponseAborted extends Error { + public readonly name = ResponseAbortedName +} + /** * Creates an AbortController tied to the closing of a ServerResponse (or other * appropriate Writable). @@ -23,7 +28,7 @@ export function createAbortController(response: Writable): AbortController { response.once('close', () => { if (response.writableFinished) return - controller.abort() + controller.abort(new ResponseAborted()) }) return controller @@ -39,7 +44,9 @@ export function createAbortController(response: Writable): AbortController { */ export function signalFromNodeResponse(response: Writable): AbortSignal { const { errored, destroyed } = response - if (errored || destroyed) return AbortSignal.abort(errored) + if (errored || destroyed) { + return AbortSignal.abort(errored ?? new ResponseAborted()) + } const { signal } = createAbortController(response) return signal