vercel/ai · warning

The streamable UI has been slow to update. This may be a bug

Error message

The streamable UI has been slow to update. This may be a bug or a performance issue or you forgot to call `.done()`.

What it means

Analogous to the streamable value warning: in development mode, createStreamableUI arms a timer that fires if the UI stream hasn't been updated or closed within HANGING_STREAM_WARNING_TIME_MS. It indicates the server never called .done() (or .update()/.append() stalled), leaving the UI stream hanging.

Source

Thrown at packages/rsc/src/streamable-ui/create-streamable-ui.tsx:73

function createStreamableUI(initialValue?: React.ReactNode) {
  let currentValue = initialValue;
  let closed = false;
  let { row, resolve, reject } = createSuspendedChunk(initialValue);

  function assertStream(method: string) {
    if (closed) {
      throw new Error(method + ': UI stream is already closed.');
    }
  }

  let warningTimeout: NodeJS.Timeout | undefined;
  function warnUnclosedStream() {
    if (process.env.NODE_ENV === 'development') {
      if (warningTimeout) {
        clearTimeout(warningTimeout);
      }
      warningTimeout = setTimeout(() => {
        console.warn(
          'The streamable UI has been slow to update. This may be a bug or a performance issue or you forgot to call `.done()`.',
        );
      }, HANGING_STREAM_WARNING_TIME_MS);
    }
  }
  warnUnclosedStream();

  const streamable: StreamableUIWrapper = {
    value: row,
    update(value: React.ReactNode) {
      assertStream('.update()');

      // There is no need to update the value if it's referentially equal.
      if (value === currentValue) {
        warnUnclosedStream();
        return streamable;
      }

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Call streamableUI.done() on all paths (wrap in try/finally).
  2. Call .update()/.append() promptly to show progress and reset the warning timer.
  3. Use .error(...) on failure so the client stream resolves.
  4. Ignore/increase the threshold if the operation is legitimately slow in dev.

Example fix

// before
const ui = createStreamableUI();
ui.append(await renderSlowComponent());
// after
const ui = createStreamableUI();
try {
  ui.append(await renderSlowComponent());
} finally {
  ui.done();
}
Defensive patterns

Strategy: try-catch

Try / catch

const ui = createStreamableUI();
try {
  ui.append(await build());
} catch (e) {
  ui.error(e);
  throw e;
} finally {
  ui.done();
}

Prevention

When it happens

Trigger: Calling createStreamableUI() in a server action/RSC handler and not calling .done() (or not updating) within the warning window; an exception thrown between creation and close; slow UI-building work exceeding the threshold.

Common situations: Generative-UI server actions that forget to close the stream on error paths; long data fetches; early returns in action handlers.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/56f96af4672240a7. Report an issue: GitHub.