Vercel AI SDK tool call persistence issue
TL;DR
When you store a tool-call/tool-result UI message as it is into the database, you should remove properties with null values like rawInput, errorText, preliminary, and providerExecuted before passing it to the validateUIMessages function.
const storedUiMessages = [
{
type: "tool-get_something",
toolCallId: "call_iW4QcfoKhNc4k2Hmt8BFKHHI",
state: "output-available",
input: { a: 1 },
output: { b: 2 },
rawInput: null,
errorText: null,
providerExecuted: null,
preliminary: null,
callProviderMetadata: { c: 3 },
},
{
type: "text",
text: "Hello, world!",
state: "done",
providerMetadata: { d: 4 },
},
];
const cleanedUiMessages = storedUiMessages.map((m) => ({
...m,
parts: m.parts.map((p) => {
const cleanedPart: any = {};
for (const [key, value] of Object.entries(p)) {
if (value !== null && value !== undefined) {
cleanedPart[key] = value;
}
}
return cleanedPart;
}),
}));
const validatedUiMessages = await validateUIMessages({
messages: cleanedUiMessages,
tools,
});
const modelMessages = convertToModelMessages(validatedUiMessages, {
tools,
});
Bug hunting method
What worked:
I approached the bug in these two ways simultaneously:
- I read the source code of the
validateUIMessagesfunction and its tests. - I asked my AI assistant (Claude 4.5 Sonnet) to create a simple JS file and try different arguments for the
validateUIMessagesfunction until it broke.
What didn’t work:
- Randomly editing parts of the code
- Asking my AI assistant to find the bug without any instructions on how to do it
- Searching the web and GitHub issues (this sometimes works, but it didn’t before I wrote this post)