[Optimistic Updates] ArgumentValidationError: Value does not match validator.

I get with my implementation of the optimistic updates this error:
Error: [CONVEX M(messages:markMessageRead)] [Request ID: c6981e014a16ba01] Server Error
ArgumentValidationError: Value does not match validator.
Path: .messageId
Value: "5b234bb4-ce93-46d7-add3-448b90a09be9"
Validator: v.id("messages")

Called by client
Error: [CONVEX M(messages:markMessageRead)] [Request ID: c6981e014a16ba01] Server Error
ArgumentValidationError: Value does not match validator.
Path: .messageId
Value: "5b234bb4-ce93-46d7-add3-448b90a09be9"
Validator: v.id("messages")

Called by client
Code (I quadruple-checked it, and it has no type errors):
const sendMessage = useMutation(
api.messages.createMessage,
).withOptimisticUpdate((localStore, args) => {
const chatId: Id<"privateChats"> = args.chatId as Id<"privateChats">;
const content = args.content;

const existingMessages = localStore.getQuery(api.messages.getMessages, {
chatId,
});
const existingChats = localStore.getQuery(api.chats.getChats);
// If we've loaded the api.messages.list query, push an optimistic message
// onto the list.
if (existingMessages !== undefined && existingChats && userInfo) {
const now = Date.now();
const newMessage: FunctionReturnType<
typeof api.messages.getMessages
>[number] = {
userId: undefined,
_id: crypto.randomUUID() as Id<"messages">,
_creationTime: now,
content,
deleted: false,
privateChatId: chatId,
from: userInfo,
readBy: [userInfo],
};
localStore.setQuery(api.messages.getMessages, { chatId }, [
...existingMessages,
newMessage,
]);
localStore.setQuery(
api.chats.getChats,
{},
existingChats.map((chat) => {
if (chat._id === chatId) {
return {
...chat,
lastMessage: {
...newMessage,
userId: userInfo._id,
},
};
} else {
return chat;
}
}),
);
}
});
const sendMessage = useMutation(
api.messages.createMessage,
).withOptimisticUpdate((localStore, args) => {
const chatId: Id<"privateChats"> = args.chatId as Id<"privateChats">;
const content = args.content;

const existingMessages = localStore.getQuery(api.messages.getMessages, {
chatId,
});
const existingChats = localStore.getQuery(api.chats.getChats);
// If we've loaded the api.messages.list query, push an optimistic message
// onto the list.
if (existingMessages !== undefined && existingChats && userInfo) {
const now = Date.now();
const newMessage: FunctionReturnType<
typeof api.messages.getMessages
>[number] = {
userId: undefined,
_id: crypto.randomUUID() as Id<"messages">,
_creationTime: now,
content,
deleted: false,
privateChatId: chatId,
from: userInfo,
readBy: [userInfo],
};
localStore.setQuery(api.messages.getMessages, { chatId }, [
...existingMessages,
newMessage,
]);
localStore.setQuery(
api.chats.getChats,
{},
existingChats.map((chat) => {
if (chat._id === chatId) {
return {
...chat,
lastMessage: {
...newMessage,
userId: userInfo._id,
},
};
} else {
return chat;
}
}),
);
}
});
5 Replies
sshader
sshaderβ€’7mo ago
Do you at some point call a mutation where the arguments come from something loaded by useQuery(api.chats.getChats)?
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
The chatId comes as a URL param But the userInfo comes from the Convex query:
const userInfo = useQuery(api.users.getUserData, {});
const userInfo = useQuery(api.users.getUserData, {});
Any other unclear arguments?
Michal Srb
Michal Srbβ€’7mo ago
Optimistic updates are likely irrelevant. The error message says that you’re calling the markMessageRead mutation with messageId argument which is an ID from a different table than the required β€œmessages”.
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
Ohhhh, I think I got it. I ran the markMessageRead mutation with a optimistic message. I think I have to check if the message is optimistic and then skip this mutation
Michal Srb
Michal Srbβ€’7mo ago
I'd disable further actions on optimistic results (like disable the "mark as read" button).