vercel/ai · error
`useUIState` must be used inside an <AI> provider.
Error message
`useUIState` must be used inside an <AI> provider.
What it means
`useUIState` reads the UI state from React context installed by the `<AI>` provider. When the context value is null there is no provider above the component in the tree, so the hook cannot return a state and the SDK throws instead of failing later with an undefined error.
Source
Thrown at packages/rsc/src/shared-client/context.tsx:131
<InternalSyncUIStateProvider.Provider
value={clientWrappedSyncUIStateAction}
>
{children}
</InternalSyncUIStateProvider.Provider>
</InternalActionProvider.Provider>
</InternalUIStateProvider.Provider>
</InternalAIStateProvider.Provider>
);
}
export function useUIState<AI extends AIProvider = any>() {
type T = InferUIState<AI, any>;
const state = React.useContext<
[T, (v: T | ((v_: T) => T)) => void] | null | undefined
>(InternalUIStateProvider);
if (state === null) {
throw new Error('`useUIState` must be used inside an <AI> provider.');
}
if (!Array.isArray(state)) {
throw new Error('Invalid state');
}
if (state[0] === undefined) {
throw new Error(
'`initialUIState` must be provided to `createAI` or `<AI>`',
);
}
return state;
}
// TODO: How do we avoid causing a re-render when the AI state changes but you
// are only listening to a specific key? We need useSES perhaps?
function useAIState<AI extends AIProvider = any>(): [
InferAIState<AI, any>,
(newState: ValueOrUpdater<InferAIState<AI, any>>) => void,
];View on GitHub (pinned to 69428b1f8b)
Solutions
- Render the component inside the `<AI>` provider tree (usually via the root layout that exports the `AI` from `createAI`)
- Move the `useUIState` call into a child component that is rendered inside `<AI>`
- In tests/stories, wrap the component with the `AI` provider before rendering
- Verify with React DevTools that `InternalUIStateProvider` context is non-null at the component
Example fix
// before
export default function Page() {
const [uiState] = useUIState(); // throws: no provider
return <Chat />;
}
// after (layout.tsx already renders <AI>)
export default function Page() {
return <ChatMessages />;
}
function ChatMessages() {
const [uiState] = useUIState();
return <div>{uiState.display}</div>;
} Defensive patterns
Strategy: validation
Validate before calling
// component-level guard before calling the hook in dependent code
function isInsideAIProvider(): boolean {
return React.useContext(InternalUIStateProvider) != null;
} Type guard
function hasUIState(
v: unknown,
): v is [T, (v: T | ((v_: T) => T)) => void] {
return Array.isArray(v) && v.length === 2;
} Try / catch
let uiState: T | undefined;
try {
const [value] = useUIState();
uiState = value;
} catch (err) {
if (String(err).includes('must be used inside an <AI> provider')) {
// render a fallback or rethrow with a clear setup message
uiState = undefined;
} else {
throw err;
}
} Prevention
- Render every page/route that uses useUIState under the layout containing <AI>
- In tests, always wrap components with the AI provider from createAI
- Watch out for portals that escape the provider tree (use context bridging if needed)
- Keep the hook call in child components, not in the same file that renders the provider
When it happens
Trigger: Calling `useUIState()` in a component rendered without an `<AI>` ancestor — e.g. in a route outside the layout that renders `<AI>`, in a test without the provider, or in a story/portal that escapes the provider tree.
Common situations: Moving a component to a new page whose layout doesn't include `<AI>`; rendering children in a React portal mounted outside the provider; unit tests (Vitest/Jest) rendering the component directly; wrapping only part of the app in `<AI>` and using the hook in the unwrapped part.
Related errors
- Invalid state
- `useSyncUIState` must be used inside an <AI> provider.
- ${method}: Value stream is already closed.
- ${method}: Value stream is locked and cannot be updated.
- .append(): The current value is not a string. Received: ${ty
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/e194ecd620f37105.
Report an issue: GitHub.