ERR_HTTP2_TRAILERS_ALREADY_SENT
Node.jsERRORCriticalHTTP/2HIGH confidence

HTTP/2 trailers already sent on this stream

Production Risk

Low — only affects servers using HTTP/2 trailers.

What this means

Thrown when stream.sendTrailers() is called more than once on the same HTTP/2 stream. Trailing headers can only be sent once per stream; a second call violates the protocol.

Why it happens
  1. 1Calling stream.sendTrailers() twice on the same stream
  2. 2Middleware that sends trailers and also delegates to a handler that sends them
How to reproduce

Triggered when sendTrailers() is called on a stream that has already sent its trailing headers.

trigger — this will error
trigger — this will error
server.on('stream', (stream) => {
  stream.respond({ ':status': 200 }, { waitForTrailers: true });
  stream.on('wantTrailers', () => {
    stream.sendTrailers({ 'x-timing': '10ms' });
    stream.sendTrailers({ 'x-extra': 'oops' }); // throws
  });
  stream.end('body');
});

expected output

Error [ERR_HTTP2_TRAILERS_ALREADY_SENT]: Trailing headers have already been sent

Fix

Call sendTrailers() exactly once inside the wantTrailers handler

WHEN Always — send all trailing headers in a single call

Call sendTrailers() exactly once inside the wantTrailers handler
stream.on('wantTrailers', () => {
  stream.sendTrailers({ 'x-timing': '10ms', 'x-extra': 'value' });
});

Why this works

Combining all trailers into one call respects the single-invocation constraint.

Code examples
Triggerjs
server.on('stream', (stream) => {
  stream.respond({ ':status': 200 }, { waitForTrailers: true });
  stream.on('wantTrailers', () => {
    stream.sendTrailers({ 'x-timing': '10ms' });
    stream.sendTrailers({ 'x-extra': 'oops' }); // throws
  });  // this triggers ERR_HTTP2_TRAILERS_ALREADY_SENT
Handle in try/catchjs
try {
  // operation that may throw ERR_HTTP2_TRAILERS_ALREADY_SENT
  riskyOperation()
} catch (err) {
  if (err.code === 'ERR_HTTP2_TRAILERS_ALREADY_SENT') {
    console.error('ERR_HTTP2_TRAILERS_ALREADY_SENT:', err.message)
  } else {
    throw err
  }
}
Defensive pattern to avoid itjs
// Validate inputs before calling the operation
function safe_err_http2_trailers_already_sent(...args) {
  // validate args here
  return performOperation(...args)
}
What not to do

Call sendTrailers() more than once per stream

Only one trailing HEADERS frame is allowed per HTTP/2 stream.

Same error in other languages
Sources
Official documentation ↗

Node.js Error Codes Documentation

Content generated with AI assistance and reviewed for accuracy. Found an error? hello@errcodes.dev

← All Node.js errors