Yes, this is expected behavior in Convex. When you use localStore.getQuery(...) inside an optimistic update, it will return undefined if the query has not yet been loaded into the client. This typically happens when you are sending the very first message to a new channel or conversation—since there are no messages yet, the query has never been run, so there is no cached result (not even an empty array).
From the documentation:
The useQuery React hook will return undefined when it is first mounted, before the query has been loaded from Convex. Once a query is loaded it will never be undefined again (even as the data reactively updates). undefined is not a valid return type for queries... You can use this as a signal for when to render loading indicators and placeholder UI.
Best-practices: UI patterns
And for optimistic updates:
This optimistic update changes the api.messages.list query for the current channel to include a new message. ... If we've loaded the api.messages.list query, push an optimistic message onto the list.
Optimistic Updates: Complex Example
So, in your code:
const existingMessages = localStore.getQuery(api.chat.getMessages, {
conversationId,
});
If this is the first message, existingMessages will be undefined. For subsequent messages, once the query has been loaded (even if it returns an empty array), getQuery will return the array (possibly empty), and your optimistic update will work as expected.
How to handle this:
You can check for undefined and treat it as an empty array when applying your optimistic update: