vercel/ai · warning

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

Error message

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

What it means

In development mode, createStreamableValue arms a timer that fires this warning if the streamable value hasn't been updated or closed within HANGING_STREAM_WARNING_TIME_MS. It signals that a stream created on the server was never finished, likely because .done(), .update(), or .append() were not called, or the value update path stalled.

Source

Thrown at packages/rsc/src/streamable-value/create-streamable-value.ts:153

  function assertStream(method: string) {
    if (closed) {
      throw new Error(method + ': Value stream is already closed.');
    }
    if (locked) {
      throw new Error(
        method + ': Value stream is locked and cannot be updated.',
      );
    }
  }

  let warningTimeout: NodeJS.Timeout | undefined;
  function warnUnclosedStream() {
    if (process.env.NODE_ENV === 'development') {
      if (warningTimeout) {
        clearTimeout(warningTimeout);
      }
      warningTimeout = setTimeout(() => {
        console.warn(
          'The streamable value 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();

  function createWrapped(initialChunk?: boolean): StreamableValue<T, E> {
    // This makes the payload much smaller if there're mutative updates before the first read.
    let init: Partial<StreamableValue<T, E>>;

    if (currentError !== undefined) {
      init = { error: currentError };
    } else {
      if (currentPatchValue && !initialChunk) {
        init = { diff: currentPatchValue };
      } else {
        init = { curr: currentValue };

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Ensure every code path calls streamableValue.done() (use try/finally).
  2. Call .update() promptly or create the value lazily right before results are available.
  3. Call .error(...) on failure paths instead of leaving the stream open.
  4. Increase/ignore the warning in dev if the operation is legitimately long-running.

Example fix

// before
const value = createStreamableValue();
const result = await doSlowWork();
value.update(result); // may never run on throw
// after
const value = createStreamableValue();
try {
  value.update(await doSlowWork());
} finally {
  value.done();
}
Defensive patterns

Strategy: try-catch

Try / catch

const value = createStreamableValue();
try {
  value.update(await work());
} catch (e) {
  value.error(e);
  throw e;
} finally {
  value.done();
}

Prevention

When it happens

Trigger: Calling createStreamableValue() on the server and neither updating nor calling .done() within the warning window; slow async work between value creation and first update; forgetting to close the stream when an error occurs.

Common situations: RSC/generative-UI handlers that create a streamable value but an exception skips the .done() call; long-running server operations exceeding the hang threshold; forgotten close on early-return code paths.

Related errors


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