Skip to main content
Stream API - Error Handling

Stream API - Error Handling

Streams fail in several ways: the network drops, the server sends an error mid-body, a transform throws, or the consumer cancels. Robust handling covers all four and releases resources.

Try, catch, and finally

const reader = response.body.getReader();

try {
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    process(value);
  }
} catch (error) {
  console.error('Stream failed', error);
} finally {
  reader.releaseLock();
}

Propagation through pipes

await source
  .pipeThrough(transform)
  .pipeTo(destination)
  .catch((error) => {
    if (error.name === 'AbortError') return;
    reportFailure(error);
  });

An error in any stage aborts the whole pipeline by default. Errors thrown inside a transform reject the pipe promise.

Rejecting a stream deliberately

new ReadableStream({
  start(controller) {
    controller.error(new Error('Upstream unavailable'));
  }
});

Distinguishing cancellation from failure

try {
  await response.body.pipeTo(sink);
} catch (error) {
  if (error.name === 'AbortError') {
    status.textContent = 'Cancelled';
  } else {
    status.textContent = 'Failed';
  }
}

User-initiated cancellation should not be reported as an error. Check the error name before displaying a message.

Partial results

When a stream fails after some chunks have been consumed, decide whether the partial data is useful. For a log viewer, keep what arrived and mark it incomplete. For a file download, discard the partial result to avoid corruption.

const chunks = [];

try {
  for await (const chunk of response.body) {
    chunks.push(chunk);
  }
  save(new Blob(chunks));
} catch (error) {
  chunks.length = 0;
  showRetry();
}

Timeouts

const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 30000);

try {
  await fetch(url, { signal: controller.signal });
} finally {
  clearTimeout(timer);
}

Checklist

  • Always release readers and writers.
  • Treat AbortError as cancellation, not failure.
  • Validate the response status before streaming the body.
  • Avoid unbounded buffering when fallback parsing is used.
  • Log enough context to diagnose where a pipeline broke.