Skip to content

useChat({ id: undefined }) recreates the Chat instance on every render, wiping messages #16226

Description

@DaisukeYoda

Description

Passing id: undefined to useChat causes the internal Chat instance, and therefore all messages and stream state, to be recreated on every render. Sent messages and in-flight streams disappear immediately, so the UI never shows a response.

This is easy to hit because id={conversationId ?? undefined} is a common pattern. The surprising part is that passing id: undefined behaves very differently from omitting id, even though the two are usually equivalent.

Versions

  • @ai-sdk/react@3.0.209
  • ai@6.0.207

Root cause

In the useChat hook (packages/react/src/use-chat.ts):

const shouldRecreateChat =
  ("chat" in options && options.chat !== chatRef.current) ||
  ("id" in options && chatRef.current.id !== options.id);

if (shouldRecreateChat) {
  chatRef.current = "chat" in options ? options.chat : new Chat(optionsWithCallbacks);
}

When id: undefined is passed:

  1. "id" in options is true, since the key exists even though its value is undefined.
  2. The Chat constructor defaults id to generateId(), so chatRef.current.id is an auto-generated UUID.
  3. As a result, chatRef.current.id !== options.id evaluates to uuid !== undefined, which is always true.

So shouldRecreateChat stays true and a fresh Chat with empty messages is created on every render.

Reproduction

function Chat({ conversationId }: { conversationId?: string }) {
  // conversationId is undefined for a brand-new chat
  const { messages, sendMessage } = useChat({ id: conversationId ?? undefined });

  // Send a message. It briefly appears, then vanishes on the next render,
  // and the streamed assistant response never shows.
  return <button onClick={() => sendMessage({ text: 'hi' })}>send</button>;
}

Expected behavior

id: undefined should behave the same as omitting id, keeping the auto-generated stable id instead of recreating the chat on every render.

Suggested fix

Guard against a nullish id:

("id" in options && options.id != null && chatRef.current.id !== options.id)

Workaround

Provide a stable fallback id:

const fallbackId = useRef(crypto.randomUUID());
const { messages } = useChat({ id: conversationId ?? fallbackId.current });

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions