vercel/ai · error

`getMutableAIState` must be called before returning from an

Error message

`getMutableAIState` must be called before returning from an AI Action. Please move it to the top level of the Action's function body.

What it means

getMutableAIState() must run while the AI Action's server function is still executing; once the action returns, the store is 'sealed'. Calling it after sealing — typically from async callbacks, event handlers, or continuation code after the action body finished — throws this error directing you to call it at the top level of the Action's function body.

Source

Thrown at packages/rsc/src/ai-state.tsx:134

>;
function getMutableAIState<AI extends AIProvider = any>(
  key: keyof InferAIState<AI, any>,
): MutableAIState<InferAIState<AI, any>[typeof key]>;
function getMutableAIState<AI extends AIProvider = any>(
  ...args: [] | [key: keyof InferAIState<AI, any>]
) {
  type AIState = InferAIState<AI, any>;
  type AIStateWithKey = typeof args extends [key: keyof AIState]
    ? AIState[(typeof args)[0]]
    : AIState;
  type NewStateOrUpdater = ValueOrUpdater<AIStateWithKey>;

  const store = getAIStateStoreOrThrow(
    '`getMutableAIState` must be called within an AI Action.',
  );

  if (store.sealed) {
    throw new Error(
      "`getMutableAIState` must be called before returning from an AI Action. Please move it to the top level of the Action's function body.",
    );
  }

  if (!store.mutationDeltaPromise) {
    const { promise, resolve } = createResolvablePromise();
    store.mutationDeltaPromise = promise;
    store.mutationDeltaResolve = resolve;
  }

  function doUpdate(newState: NewStateOrUpdater, done: boolean) {
    if (args.length > 0) {
      if (typeof store.currentState !== 'object') {
        const key = args[0];
        throw new Error(
          `You can't modify the "${String(
            key,
          )}" field of the AI state because it's not an object.`,

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Move getMutableAIState() to the top level of the AI Action function body, before any `return`.
  2. Capture the mutable state handle early, then perform mutations with that handle before the action returns.
  3. Do not call getMutableAIState from background tasks, timers, or post-return continuations; use setAIState patterns or redesign so mutations happen within the action's lifetime.

Example fix

// before
export async function myAction() {
  const result = await generate(input);
  return result;
}
// later, outside: getMutableAIState().done(...)  // throws

// after
export async function myAction() {
  const state = getMutableAIState();
  const result = await generate(input);
  state.update({ ...state.get(), last: result });
  state.done();
  return result;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// inside an AI Action, before any await/return:
if (typeof getMutableAIState !== 'function') {
  throw new Error('getMutableAIState is only available inside AI Actions');
}

Try / catch

try {
  const state = getMutableAIState();
  // ...mutate and state.done() before returning
} catch (e) {
  if (e instanceof Error && e.message.includes('must be called before returning from an AI Action')) {
    // restructure: move the call to the top of the action body
  } else throw e;
}

Prevention

When it happens

Trigger: Calling getMutableAIState inside setTimeout/Promise callbacks that run after the action returned; invoking it inside nested async continuations after awaiting past the action's synchronous scope; calling it from non-Action server code where the store was already sealed.

Common situations: Deferring state mutation with `.then()` after returning UI; calling getMutableAIState in a helper invoked after `return generate(...)`; using it in route handlers instead of AI Actions.

Related errors


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