shadcn-ui/ui · error

Only continuation turns materialize from a transcript.

Error message

Only continuation turns materialize from a transcript.

What it means

materializeDeferredTurn() rebuilds a deferred (continuation) turn's events from a live transcript; it requires turns[turnIndex].resolve to be set. Calling it on a regular, already-resolved turn is a logic error because that turn has no deferred resolution to run. This is an internal invariant guard, not a user-input error.

Source

Thrown at packages/helpers/src/core/chat.ts:496

          : undefined,
      ...(call.approval
        ? {
            approved: approved === true,
            denied: approved === false,
          }
        : {}),
    } as PendingToolCall<TOOLS>
  }

  function materializeDeferredTurn(
    turnIndex: number,
    messages: MESSAGE[]
  ): ResolvedTurn {
    const turn = turns[turnIndex]
    const resolve = turn.resolve

    if (!resolve) {
      throw new Error("Only continuation turns materialize from a transcript.")
    }

    const events = [...turn.events]

    // The client merges a continuation into the prior assistant message as a
    // new step. The step boundary keeps the resolved tool call out of the
    // latest step so automatic sending does not retrigger on it.
    events.push({ kind: "step-start" })

    const pending = resolvePendingToolCalls(turnIndex, messages)

    if (!pending.length) {
      devWarn(
        "A continuation turn resolved without a pending tool call. Its toolCall context is empty."
      )
    }

    for (const call of pending) {

View on GitHub (pinned to efac598707)

Solutions

  1. Guard callers so materializeDeferredTurn only runs when turns[turnIndex].resolve is defined.
  2. Re-check findNextAssistantTurnIndex logic if this fires from the built-in transport.
  3. Treat materializeDeferredTurn as internal API; do not call it from adapter or app code.

Example fix

// before
materializeDeferredTurn(turnIndex, messages)

// after
if (turns[turnIndex]?.resolve) {
  materializeDeferredTurn(turnIndex, messages)
}
Defensive patterns

Strategy: validation

Validate before calling

const turn = turns[turnIndex]
if (!turn?.resolve) {
  // not a continuation turn; nothing to materialize
  return
}
materializeDeferredTurn(turnIndex, messages)

Type guard

const isDeferredTurn = (t: unknown): t is { resolve: (...a: unknown[]) => unknown } =>
  Boolean(t && typeof (t as { resolve?: unknown }).resolve === "function")

Prevention

When it happens

Trigger: Internal mis-indexing where findNextAssistantTurnIndex points at a non-deferred turn; calling materializeDeferredTurn directly with a wrong turnIndex; a transcript whose continuation marker was cleared.

Common situations: Bugs in turn-index resolution; custom adapters that invoke materializeDeferredTurn; corrupt transcript state from partial persistence.

Related errors


AI-assisted analysis of shadcn-ui/ui@efac598707 (2026-08-12). Data as JSON: /api/errors/ad1ed74d38de9d04. Report an issue: GitHub.