Diagnostics

LUM5009: Boundary still pending when streamTimeout expired

A <Suspense> boundary was still waiting for data when lumiose({ streamTimeout }) expired.

Severity: error · Category: routing and server (5xxx)

Reported by: the server runtime, to the <ErrorBoundary> around the waiting component (or to the boundary itself), to onRequestError with phase "stream", and to the server log.

#What it means

A page render waits for its pending <Suspense> boundaries for streamTimeout, 30 seconds by default. When it expires, every async component still waiting fails with this error: the nearest <ErrorBoundary> around it renders its fallback into the stream. A boundary with no <ErrorBoundary> inside keeps its <Suspense> fallback. Then the response ends, so a data source that never answers cannot hold the connection open. In production the <ErrorBoundary> fallback receives a generic error with a digest, as for any error (the log has the original).

A response that does not stream (response().setStreaming(false)) waits for the same timeout; there a boundary with no <ErrorBoundary> fails the page, as any error inside it would.

#Example

tsx
export default function Dashboard() {
  return (
    <Suspense fallback={<p>Loading…</p>}>
      <ErrorBoundary fallback={<p>The report is taking too long. Try again later.</p>}>
        <Report />
      </ErrorBoundary>
    </Suspense>
  );
}

async function Report() {
  const report = await fetchJSON<Report>("/api/report"); // the service never answers
  return <ReportTable report={report} />;
}

#How to fix it

Find the data source that did not answer in time (onRequestError receives the route). Give it its own timeout or cache, render an <ErrorBoundary> around the component so readers see a useful fallback, or raise the limit with lumiose({ streamTimeout: "60s" }). streamTimeout: false waits as long as the client stays connected. Prerendering ignores the timeout.