Skip to content

fix: sanitize null providerExecuted in tool-call parts to prevent ModelMessage validation error#3100

Open
octo-patch wants to merge 1 commit intoonlook-dev:mainfrom
octo-patch:fix/issue-2888-null-provider-executed-model-message
Open

fix: sanitize null providerExecuted in tool-call parts to prevent ModelMessage validation error#3100
octo-patch wants to merge 1 commit intoonlook-dev:mainfrom
octo-patch:fix/issue-2888-null-provider-executed-model-message

Conversation

@octo-patch
Copy link
Copy Markdown

@octo-patch octo-patch commented Apr 23, 2026

Problem

Closes #2888

When a conversation with tool calls is loaded from the database and the AI stream resumes, the app throws:

Invalid prompt: The messages must be a ModelMessage[]. If you have passed a UIMessage[], you can use convertToModelMessages to convert them.

Root Cause

Tool-call parts are stored as JSONB in the database. JSONB permits JSON null as a valid value for any field — so providerExecuted can be stored as null when it wasn't explicitly set (e.g. from older records or a race during streaming).

In convertToModelMessages (AI SDK v5, src/ui/convert-to-model-messages.ts, line ~5915), the function passes providerExecuted directly from the UIMessage part into the ModelMessage content:

content.push({
  type: "tool-call",
  toolCallId: part.toolCallId,
  toolName,
  input: ...,
  providerExecuted: part.providerExecuted,  // null from DB passes through here
});

standardizePrompt then validates this against modelMessageSchema using Zod v4's z.boolean().optional(). In Zod v4, .optional() allows undefined but rejects null — causing the crash.

Fix

ensureToolCallResults (in packages/ai/src/stream/index.ts) already processes all tool-call parts on every assistant message before convertToModelMessages runs. The fix adds a null-to-undefined sanitization step there:

// DB JSONB can store JSON null; z.boolean().optional() rejects null
const rawProviderExecuted = (toolPart as { providerExecuted?: boolean | null }).providerExecuted;
const sanitizedPart =
  rawProviderExecuted === null
    ? ({ ...toolPart, providerExecuted: undefined } as ToolUIPart)
    : toolPart;

This converts null → undefined for providerExecuted on every tool-call part before the AI SDK validates it, fixing the crash without affecting any other behavior.

Test plan

  • Start a conversation that uses tool calls (e.g. code actions)
  • Close and reopen the project to reload the conversation from DB
  • Verify the chat resumes and streamText is called without the "Invalid prompt" error
  • Confirm existing tool call behavior (inline diffs, apply changes) still works correctly

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of tool call execution states to ensure consistent data processing and prevent validation errors during tool result processing.

…conversion

DB JSONB storage can persist providerExecuted as JSON null for tool-call parts.
The AI SDK's standardizePrompt validates messages using z.boolean().optional(),
which accepts undefined but rejects null (Zod v4 strict behavior), causing the
'Invalid prompt: The messages must be a ModelMessage[]' error on resume.

ensureToolCallResults now converts providerExecuted: null to undefined for all
tool-call parts before convertToModelMessages runs, preventing the Zod failure.

Fixes onlook-dev#2888
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 23, 2026

Someone is attempting to deploy a commit to the Onlook Team on Vercel.

A member of the Team first needs to authorize it.

@vercel vercel Bot temporarily deployed to Preview – docs-onlook April 23, 2026 04:26 Inactive
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs-onlook Skipped Skipped Apr 23, 2026 4:26am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 830045c0-0b73-485c-b2d0-c2039e297e87

📥 Commits

Reviewing files that changed from the base of the PR and between a242be5 and b1eaa8e.

📒 Files selected for processing (1)
  • packages/ai/src/stream/index.ts

📝 Walkthrough

Walkthrough

The ensureToolCallResults function in the stream handler now includes sanitization logic that converts null providerExecuted values to undefined during tool part mapping. This sanitized part is then used consistently for subsequent stub output determination and field extraction, removing null provider flags before downstream validation.

Changes

Cohort / File(s) Summary
Tool Call Sanitization
packages/ai/src/stream/index.ts
Modified ensureToolCallResults to sanitize providerExecuted field by converting null to undefined in tool parts during second-pass mapping. Updated stub output logic to use sanitized parts for toolCallId and state extraction, ensuring null provider flags do not propagate to downstream schema validation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Poem

🐰 A rabbit hops through null and void,
Converting flags that once annoyed,
With undefined's gentler touch,
Schema validation loves it much! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: sanitizing null providerExecuted values in tool-call parts to prevent validation errors.
Description check ✅ Passed The PR description is comprehensive, covering problem statement, root cause analysis, implementation details, and a test plan aligned with the description template.
Linked Issues check ✅ Passed The code changes directly address the linked issue #2888 by sanitizing null providerExecuted values to prevent the ModelMessage validation error when resuming AI streams.
Out of Scope Changes check ✅ Passed All changes are scoped to the null sanitization fix in ensureToolCallResults; no out-of-scope modifications to other areas are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] the title of bug report

1 participant