{"record":{"id":"2ee6e5047f5fa2c5","repo":"vercel/ai","slug":"getmutableaistate-must-be-called-before-returnin","errorCode":null,"errorMessage":"`getMutableAIState` must be called before returning from an AI Action. Please move it to the top level of the Action's function body.","messagePattern":"`getMutableAIState` must be called before returning from an AI Action\\. Please move it to the top level of the Action's function body\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/rsc/src/ai-state.tsx","lineNumber":134,"sourceCode":">;\nfunction getMutableAIState<AI extends AIProvider = any>(\n  key: keyof InferAIState<AI, any>,\n): MutableAIState<InferAIState<AI, any>[typeof key]>;\nfunction getMutableAIState<AI extends AIProvider = any>(\n  ...args: [] | [key: keyof InferAIState<AI, any>]\n) {\n  type AIState = InferAIState<AI, any>;\n  type AIStateWithKey = typeof args extends [key: keyof AIState]\n    ? AIState[(typeof args)[0]]\n    : AIState;\n  type NewStateOrUpdater = ValueOrUpdater<AIStateWithKey>;\n\n  const store = getAIStateStoreOrThrow(\n    '`getMutableAIState` must be called within an AI Action.',\n  );\n\n  if (store.sealed) {\n    throw new Error(\n      \"`getMutableAIState` must be called before returning from an AI Action. Please move it to the top level of the Action's function body.\",\n    );\n  }\n\n  if (!store.mutationDeltaPromise) {\n    const { promise, resolve } = createResolvablePromise();\n    store.mutationDeltaPromise = promise;\n    store.mutationDeltaResolve = resolve;\n  }\n\n  function doUpdate(newState: NewStateOrUpdater, done: boolean) {\n    if (args.length > 0) {\n      if (typeof store.currentState !== 'object') {\n        const key = args[0];\n        throw new Error(\n          `You can't modify the \"${String(\n            key,\n          )}\" field of the AI state because it's not an object.`,","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/rsc/src/ai-state.tsx#L116-L152","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Move getMutableAIState() to the top level of the AI Action function body, before any `return`.","Capture the mutable state handle early, then perform mutations with that handle before the action returns.","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."],"exampleFix":"// before\nexport async function myAction() {\n  const result = await generate(input);\n  return result;\n}\n// later, outside: getMutableAIState().done(...)  // throws\n\n// after\nexport async function myAction() {\n  const state = getMutableAIState();\n  const result = await generate(input);\n  state.update({ ...state.get(), last: result });\n  state.done();\n  return result;\n}","handlingStrategy":"try-catch","validationCode":"// inside an AI Action, before any await/return:\nif (typeof getMutableAIState !== 'function') {\n  throw new Error('getMutableAIState is only available inside AI Actions');\n}","typeGuard":null,"tryCatchPattern":"try {\n  const state = getMutableAIState();\n  // ...mutate and state.done() before returning\n} catch (e) {\n  if (e instanceof Error && e.message.includes('must be called before returning from an AI Action')) {\n    // restructure: move the call to the top of the action body\n  } else throw e;\n}","preventionTips":["Call getMutableAIState() as the first statement of every AI Action.","Never invoke it inside timers, .then() callbacks, or after `return`.","Keep mutations synchronous within the action body and finish with .done().","Do not use getMutableAIState outside server actions."],"tags":["rsc","ai-state","lifecycle","react-server-components"],"backgroundTag":"mutable-ai-state-sealed","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}