Getting uncaughtException: Error: aborted with SSE after client browser close the connection
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 142k
- Forks
- 32.5k
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 351
Description
Link to the code that reproduces this issue
https://codesandbox.io/p/sandbox/n-vlt865
To Reproduce
1- Create a route on the api folder inside the app directory like this:
export const runtime = "edge";
export async function POST() {
console.log("Route: /api/sse triggered");
const responseStream = new TransformStream();
return new Response(responseStream.readable, {
headers: {
"Content-Type": "text/event-stream",
Connection: "keep-alive",
"Cache-Control": "no-cache, no-transform",
"X-Accel-Buffering": "no",
"Content-Encoding": "none",
"Access-Control-Allow-Origin": "*",
},
});
}
2- Create a client to connect to the SSE route
'use client';
import {
EventSourceMessage,
EventStreamContentType,
fetchEventSource,
} from '@microsoft/fetch-event-source';
class RetriableError extends Error {}
class FatalError extends Error {}
class SSEService extends EventTarget {
constructor() {
super();
this.connect();
}
connect = async () => {
await fetchEventSource('/api/sse', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
foo: 'bar',
}),
onmessage: this.onMessage,
onclose: this.onClose,
onopen: this.onOpen,
onerror: this.onError,
openWhenHidden: true,
});
};
onMessage(msg: EventSourceMessage) {
console.log(msg);
}
onClose() {
console.log('closed');
// if the server closes the connection unexpectedly, retry:
throw new RetriableError();
}
async onOpen(response: Response) {
console.log('open', response);
if (
response.ok &&
response.headers.get('content-type') === EventStreamContentType
) {
return; // everything's good
} else if (
response.status >= 400 &&
response.status < 500 &&
response.status !== 429
) {
// client-side errors are usually non-retriable:
throw new FatalError();
} else {
throw new RetriableError();
}
}
onError(err: Error) {
console.log('error', err);
if (err instanceof FatalError) {
throw err; // rethrow to stop the operation
} else {
// do nothing to automatically retry. You can also
// return a specific retry interval here.
}
}
}
export default SSEService;
3- Use the client to connect inside a page
"use client";
import React, { useEffect, useRef } from "react";
import SSEService from "./services/SSEService";
export default function Home() {
const ref = useRef<SSEService>();
useEffect(() => {
if (!ref.current) {
ref.current = new SSEService();
}
}, []);
return <div>page index</div>;
}
4- Refresh the page and you will get this error on the server console
⨯ uncaughtException: Error: aborted
at connResetException (node:internal/errors:705:14)
at abortIncoming (node:_http_server:642:17)
at socketOnClose (node:_http_server:636:3)
at Socket.emit (node:events:525:35)
at TCP.<anonymous> (node:net:301:12)
at TCP.callbackTrampoline (node:internal/async_hooks:130:17) {
code: 'ECONNRESET'
}
Current vs. Expected behavior
Close gracefully the connection
Verify canary release
- I verified that the issue exists in the latest Next.js canary release
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 22.6.0: Fri Sep 15 13:41:28 PDT 2023; root:xnu-8796.141.3.700.8~1/RELEASE_ARM64_T6020
Binaries:
Node: 16.20.1
npm: 8.19.4
Yarn: 1.22.19
pnpm: N/A
Relevant Packages:
next: 13.5.3
eslint-config-next: 13.5.3
react: 18.2.0
react-dom: 18.2.0
typescript: 4.9.5
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
App Router
Additional context
No response
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the linked CodeSandbox and reproducing the refresh or browser-close behavior against the App Router /api/sse POST route. Read the edge route and the SSEService client disconnect path, then confirm when the server emits ECONNRESET. Done means the connection closes without an uncaughtException while the SSE route continues to work.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, next.js, node.js, react
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100