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.

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.

Appendix: 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)