Most Next.js AI SDK bugs are boundary bugs: server code in the client, or client hooks in server components. The rules are simple.
1. streamText and generateText run in route handlers, server actions, or server components. Never in a 'use client' component. The provider key lives server-side; calling from the client would expose it and fail on missing env.
2. useChat, useCompletion, and useObject are client hooks. They must live in a file with 'use client' at the top. Putting them in a server component is a build error.
3. The page pattern: a server component (or plain page) renders a 'use client' chat component. The chat component owns useChat and talks to /api/chat over HTTP. The route handler owns streamText.
4. Only serializable data crosses the boundary. UIMessage objects are plain JSON and cross fine. Functions, class instances, and streams do not.
5. Do not import server-only modules (ai provider factories with keys are fine, but never the key itself) into client components. If a client file imports something that reads process.env keys at module scope, that key can end up in the client bundle.
6. Server Actions are an alternative to route handlers for simple calls, but streaming from a server action has sharp edges (see the docs troubleshooting on server actions in client components). Prefer route handlers for streaming chat.
7. generateId from ai is safe to use anywhere, including client components, for chat IDs and message keys.