Skip to content

Serialize undefined tool output to null in UI message chunks - #15855

Merged
gr2m merged 3 commits into
vercel:mainfrom
nwalters512:tool-result-undefined-output
Jun 17, 2026
Merged

Serialize undefined tool output to null in UI message chunks#15855
gr2m merged 3 commits into
vercel:mainfrom
nwalters512:tool-result-undefined-output

Conversation

@nwalters512

@nwalters512 nwalters512 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Background

See #15854.

Summary

toUIMessageChunk(...) now coerces undefined tool output to null so that the property survives being round-tripped through JSON.

Manual Verification

I made the change in my own application's node_modules and verified that the issue I described in the linked issue was resolved.

Checklist

  • All commits are signed (PRs with unsigned commits cannot be merged)
  • Tests have been added / updated (for bug fixes / features)
  • Documentation has been added / updated (for bug fixes / features)
  • A patch changeset for relevant packages has been added (for bug fixes / features - run pnpm changeset in the project root)
  • I have reviewed this pull request (self-review)

Future Work

This prevents invalid data from being produced in streams, but doesn't do anything to handle bad data that was already produced. If maintainers would be onboard, I'd like to make a few more changes to make this work seamlessly for people:

  • Update the 'tool-output-available' schema in uiMessageChunkSchema to have a .default(null) on the output schema.
  • Update the 'output-available' schemas in validateUIMessages to have a .default(null) on the output schema
  • Update convertToModelMessages to pre-normalize undefined tool output to null before createToolModelOutput is called.

I'd be happy to increase the scope of this PR to include this, or move it to its own PR.

As it stands, I've already stored the "bad" data in my application's database, so even if this PR is merged, I'd need application-level compatibility with old messages. It'd be great to be able to shift this into ai itself, and I bet others could benefit from this too. If nothing else, it'd help ensure a seamless upgrade path for others looking to move from Zod 3 to Zod 4.

Related Issues

Fixes #15854

@gr2m gr2m left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Signed, full CI green, correct undefined→null coercion with regression test.

@gr2m
gr2m merged commit 6a2caf9 into vercel:main Jun 17, 2026
49 of 50 checks passed
@gr2m gr2m added the backport Admins only: add this label to a pull request in order to backport it to the prior version label Jun 17, 2026
github-actions Bot added a commit that referenced this pull request Jun 17, 2026
@github-actions github-actions Bot removed the backport Admins only: add this label to a pull request in order to backport it to the prior version label Jun 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Backport to release-v6.0 created but has conflicts: #16240

gr2m added a commit that referenced this pull request Jun 17, 2026
On v6.0 the tool-output-available chunk is produced in stream-text.ts
(to-ui-message-chunk.ts does not exist here). Apply the undefined->null
coercion there and drop the cherry-picked v7-only files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gr2m added a commit that referenced this pull request Jun 17, 2026
## Background

Adding the `backport` label to a **merged fork PR** fires the `labeled`
event. For `pull_request` events originating from a fork, GitHub forces
`GITHUB_TOKEN` to read-only regardless of the job's declared
`pull-requests: write`. The fork-guidance `gh pr comment` then fails
with `GraphQL: Resource not accessible by integration (addComment)`, and
under `set -euo pipefail` the step aborts before the intended `skip`
(exit 0) — failing the whole job.

Example failure:
https://github.com/vercel/ai/actions/runs/27725203720/job/82019805216
(firing on #15855).

## Summary

Make the fork-guidance comment best-effort (`|| echo ::warning::…`) so
the job still exits cleanly via `skip`. The actual backport path
(`workflow_dispatch`) is unaffected.

## Manual Verification

The change is shell-only. The failing run above hit the `gh pr comment`
line and aborted; with `|| echo`, the script proceeds to `skip` and
exits 0.

## Checklist

- [x] All commits are signed
- [ ] Tests have been added / updated
- [ ] Documentation has been added / updated
- [ ] A _patch_ changeset for relevant packages has been added
- [x] I have reviewed this pull request (self-review)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gr2m added a commit that referenced this pull request Jun 17, 2026
#16240)

This is an automated backport of #15855 to the release-v6.0 branch. FYI
@nwalters512

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Gregor Martynus <39992+gr2m@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@nwalters512

nwalters512 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

@gr2m thanks for merging this! Did you take a look at the "future work" section I had above? Obviously I won't be able to do any of that in this PR now that it's merged, but I'd be happy to open a follow-up if you'd be on board with those changes.

On second thought: I'll just open the PR so this doesn't get lost. If you don't like the changes, let me know or feel free to close it outright!

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UI stream can emit invalid tool-output-available chunks when a tool returns undefined

3 participants