expressjs/express · warning · Error
ECONNABORTED
ECONNABORTED
Error message
Request aborted
What it means
Express's internal `sendfile` helper (lib/response.js:929-936) wraps the `send` file-streaming module for `res.sendFile()`/`res.download()`. When the underlying stream emits an abort — or finishes with an ECONNRESET (line 969 routes that to `onaborted`) — Express synthesizes `new Error('Request aborted')` with `code: 'ECONNABORTED'` and passes it to your sendFile callback (or to `next()` if none). It means the client disconnected before the file transfer completed; the server side did nothing wrong.
Source
Thrown at lib/response.js:933
if (err) return req.next(err);
self.send(str);
};
// render
app.render(view, opts, done);
};
// pipe the send file stream
function sendfile(res, file, options, callback) {
var done = false;
var streaming;
// request aborted
function onaborted() {
if (done) return;
done = true;
var err = new Error('Request aborted');
err.code = 'ECONNABORTED';
callback(err);
}
// directory
function ondirectory() {
if (done) return;
done = true;
var err = new Error('EISDIR, read');
err.code = 'EISDIR';
callback(err);
}
// errors
function onerror(err) {
if (done) return;
done = true;View on GitHub ↗ (pinned to a3714473fe)
Solutions
- Handle it in the sendFile/download callback: if (err && err.code === 'ECONNABORTED') just log at debug level and return — the client is gone, do not attempt another response.
- In global error middleware, check `err.code === 'ECONNABORTED'` (and `res.headersSent`) and swallow/log instead of rendering a 500.
- If aborts are frequent and unwanted, raise proxy/load-balancer timeouts (e.g. nginx `proxy_read_timeout`, ALB idle timeout) above your largest transfer time.
- For very large files, serve via a static file server/CDN or `express.static` with range support so retries are cheap.
Example fix
// before
res.download('/data/report.zip', function (err) {
if (err) next(err); // turns every client cancel into a 500 in logs
});
// after
res.download('/data/report.zip', function (err) {
if (err) {
if (err.code === 'ECONNABORTED') {
debug('client aborted download');
return; // client disconnected — nothing to send
}
return next(err);
}
}); Defensive patterns
Strategy: try-catch
Type guard
function isConnAborted(err) {
return err != null && typeof err === 'object' && err.code === 'ECONNABORTED';
} Try / catch
res.sendFile(filePath, function (err) {
if (err) {
if (err.code === 'ECONNABORTED') {
// Client disconnected mid-transfer; nothing to send. Log and stop.
console.warn('client aborted download of %s', filePath);
return;
}
next(err);
}
}); Prevention
- Always pass a callback to res.sendFile/res.download so transfer errors are delivered to you instead of crashing or being silently dropped
- Treat ECONNABORTED as a client-side event, not a server fault — log it at low severity and do not attempt to write another response
- Check res.headersSent before trying to send an error response in the callback, since headers are usually already sent when the abort happens
- Set sensible timeouts (server.requestTimeout, reverse-proxy timeouts) so slow clients abort predictably rather than hanging sockets
- Expect aborts under load and on mobile networks; make download endpoints idempotent so a retried request is safe
When it happens
Trigger: Calling `res.sendFile(path, cb)` or `res.download(path, cb)` while the client closes the socket mid-transfer: the user hits stop/navigates away, an HTTP client times out or calls `request.abort()`, a proxy (nginx, ELB) kills the upstream connection, or a video/PDF viewer issues range requests and drops earlier ones. It is delivered via the callback, or hits your error middleware if you passed none.
Common situations: Log noise from users cancelling large downloads or streaming video with range requests; load balancers with idle timeouts shorter than the file transfer time; frontend fetch calls with AbortController timeouts; health checkers/scanners that disconnect immediately. Developers often mistake it for a server bug and crash the process by re-throwing it, or double-respond by treating it as a case needing an error page.
Related errors
- Invalid status code: ${JSON.stringify(code)}. Status code mu
- path argument is required to res.sendFile
- path must be a string to res.sendFile
- path must be absolute or specify root to res.sendFile
- EISDIR
AI-assisted analysis of expressjs/express@a3714473fe (2026-07-31).
Data as JSON: /data/errors/265a834aab0d9a54.json.
Report an issue: GitHub ↗.