close
Skip to content

fix(core): keep array textStream valid when the first chunk has elements - #21559

Merged
abhiaiyer91 merged 1 commit into
mastra-ai:mainfrom
uuzzrm:fix/array-textstream-valid-json
Aug 14, 2026
Merged

fix(core): keep array textStream valid when the first chunk has elements#21559
abhiaiyer91 merged 1 commit into
mastra-ai:mainfrom
uuzzrm:fix/array-textstream-valid-json

Conversation

@uuzzrm

@uuzzrm uuzzrm commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Description

createJsonTextStreamTransformer produced invalid JSON for an array textStream whenever the first object chunk already contained elements. Coarse or batched provider deltas deliver the array-so-far as the first chunk, and the transformer's first-chunk fast path emitted a fully-closed array ([{\"a\":1}]) that later chunks then appended to:

chunks: [{a:1}]  [{a:1},{a:2}]  [{a:1},{a:2},{a:3}]
output: [{"a":1}],{"a":2},{"a":3}]   // JSON.parse throws

This fix holds the first array chunk back and only decides at flush time:

  • if a second chunk arrives, the buffered chunk was just the first slice, so it switches to incremental streaming;
  • if the stream ends after one chunk, it emits the complete array as a single closed JSON string.

That keeps the existing single-chunk textStream contract (one complete array in one chunk) intact, fixes the multi-chunk case, and also makes a lone empty chunk emit [] instead of a bare [.

after:  [{"a":1},{"a":2},{"a":3}]   // valid

Related issue(s)

Fixes #18758

Note: #18759 targets the same bug with an always-incremental approach that changes single-chunk chunk boundaries and currently fails an existing loop test. This PR keeps those boundaries untouched instead.

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test update

Checklist

  • I have linked the related issue(s) in the description above
  • I have made corresponding changes to the documentation (if applicable)
  • I have added tests that prove my fix is effective or that my feature works
  • I have addressed all Coderabbit comments on this PR

ELI5

This fix keeps streamed array data as one valid JSON array. It handles both multi-chunk streams and streams that contain only one chunk.

Changes

  • Buffer the first array chunk in createJsonTextStreamTransformer.
  • Stream buffered elements incrementally when later chunks arrive.
  • Emit a complete JSON array when the stream ends after one chunk.
  • Emit [] for a lone empty chunk.
  • Add tests for batched, single-chunk, empty, and incremental array output.
  • Add a patch changeset for @mastra/core.

createJsonTextStreamTransformer emitted a fully-closed array whenever the
first object chunk arrived with elements already in it, then kept appending
later elements plus a closing bracket. Coarse or batched provider deltas hit
this path and produced invalid JSON like [{"a":1}],{"a":2}] that JSON.parse
rejects.

Hold the first array chunk back and only decide at flush: if a second chunk
arrives, stream incrementally; if the stream ends after one chunk, emit the
complete array as a single closed JSON string. This preserves the existing
single-chunk textStream contract and also fixes a latent case where a lone
empty chunk produced a bare [.

Fixes mastra-ai#18758.
@changeset-bot

changeset-bot Bot commented Aug 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b2cc3bf

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 25 packages
Name Type
@mastra/core Patch
@mastra/code-sdk Patch
mastracode Patch
@mastra/factory Patch
@mastra/mcp-docs-server Patch
@internal/playground Patch
@mastra/client-js Patch
@mastra/opencode Patch
@mastra/longmemeval Patch
mastra Patch
@mastra/deployer-cloud Patch
@mastra/react Patch
@mastra/playground-ui Patch
@mastra/server Patch
@mastra/deployer Patch
create-mastra Patch
create-factory Patch
@mastra/express Patch
@mastra/fastify Patch
@mastra/hono Patch
@mastra/koa Patch
@mastra/nestjs Patch
@mastra/next Patch
@mastra/tanstack-start Patch
@mastra/temporal Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Aug 14, 2026

Copy link
Copy Markdown

@uuzzrm is attempting to deploy a commit to the Mastra Team on Vercel.

A member of the Team first needs to authorize it.

@dane-ai-mastra dane-ai-mastra Bot added the complexity: low Low-complexity PR label Aug 14, 2026
@dane-ai-mastra

Copy link
Copy Markdown
Contributor

PR triage

Linked issue check passed (#18758).

Mastra uses CodeRabbit for automated code reviews. Please address all feedback from CodeRabbit by either making changes to your PR or leaving a comment explaining why you disagree with the feedback. Since CodeRabbit is an AI, it may occasionally provide incorrect feedback.


PR complexity score

Factor Value Score impact
Files changed 3 +6
Lines changed 123 +6
Author merged PRs 2 -2
Test files changed Yes -10
Final score 0

Applied label: complexity: low


Changed test gate

Changed Test Gate is pending. The Changed Test Gate / changed-tests check will update the test label when it completes.

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bef49d23-31be-4b4b-90e8-23503d52184c

📥 Commits

Reviewing files that changed from the base of the PR and between be3298b and b2cc3bf.

📒 Files selected for processing (3)
  • .changeset/giant-rabbits-ask.md
  • packages/core/src/stream/base/output-format-handlers.test.ts
  • packages/core/src/stream/base/output-format-handlers.ts

Walkthrough

The array textStream transformer now buffers the first array chunk, distinguishes complete arrays from incremental streams, and emits valid JSON. Tests cover batched, single-chunk, empty, and fine-grained array output.

Changes

Array textStream JSON handling

Layer / File(s) Summary
Buffer and flush the first array chunk
packages/core/src/stream/base/output-format-handlers.ts
The transformer buffers the first array chunk. It emits buffered elements incrementally when later chunks arrive and emits a complete array during flush.
Validate array stream output
packages/core/src/stream/base/output-format-handlers.test.ts, .changeset/giant-rabbits-ask.md
Tests verify valid JSON and chunk boundaries for batched, single-chunk, empty, and incremental arrays. A patch changeset records the fix.

Estimated code review effort: 2 (Simple) | ~15 minutes

Merge Risk: ⚪ Minimal · up to b2cc3

This localized fix preserves the existing single-chunk behavior while correcting multi-chunk array streaming, and no actionable merge-blocking risk remains beyond normal checks and review.

Suggested reviewers: wardpeet, taofeeq-deru, abhiaiyer91

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the array textStream fix and uses an imperative action, although it exceeds the preferred 50-character length.
Linked Issues check ✅ Passed The changes fix invalid JSON for progressive array chunks and add coverage for batched, single-chunk, empty, and incremental streams required by issue #18758.
Out of Scope Changes check ✅ Passed All changes are limited to the transformer fix, focused tests, and its patch-release changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@mastra-platform mastra-platform Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: approve

Findings

  • Correctness: No blocking findings. createJsonTextStreamTransformer now buffers the first array snapshot, preserving a complete one-chunk JSON array while switching to incremental emission when a second snapshot arrives. This prevents a closed first array from being followed by appended elements and keeps both coarse and fine-grained streams parseable (packages/core/src/stream/base/output-format-handlers.ts:766-824).
  • Tests: The regression coverage meaningfully exercises a non-empty first snapshot, a complete single snapshot, a lone empty snapshot, and fine-grained growth (packages/core/src/stream/base/output-format-handlers.test.ts:1255-1302).
  • Scope: The runtime change, focused tests, and patch changeset are coherent and limited to issue #18758.
  • Pattern consistency: The implementation remains inside the existing output-format transformer and retains snapshot-length delta emission used by the prior implementation; no new abstraction or API contract is introduced.

Verification

  • env -u GH_TOKEN -u GITHUB_TOKEN pnpm install — passed; only expected missing pre-build CLI symlink warnings.
  • env -u GH_TOKEN -u GITHUB_TOKEN pnpm build:core — passed (14/14 tasks).
  • env -u GH_TOKEN -u GITHUB_TOKEN pnpm --filter ./packages/core exec vitest run src/stream/base/output-format-handlers.test.ts --reporter=dot --bail 1 — passed (1 file, 40 tests, no type errors).
  • env -u GH_TOKEN -u GITHUB_TOKEN pnpm --filter ./packages/core exec vitest run src/stream/base --reporter=dot --bail 1 — passed (5 files, 100 tests, no type errors).
  • env -u GH_TOKEN -u GITHUB_TOKEN pnpm --filter ./packages/core check — passed.
  • gh pr checks 21559 — CodeRabbit and security/label checks passed. Two Vercel deployment checks report authorization-required failures; these do not exercise the changed core package and are unrelated to this diff.

Existing review disposition

  • CodeRabbit correctness/risk review — addressed: The bot found no actionable merge-blocking defect and rated the localized change minimal risk; direct inspection and focused execution agree.
  • CodeRabbit ESLint installation warning — refuted as a PR finding: This was a bot-tool installation failure rather than a reported source violation. The package typecheck and the full focused stream/base suite pass locally.
  • CodeRabbit “fix failing CI” finishing touch — refuted as a code finding: The only failing checks are Vercel authorization gates for docs/playground deployments, while this PR changes only core transformer code, its tests, and a changeset.
  • dane-ai-mastra triage — addressed: Linked issue validation passed, complexity is low, tests are included, and the CodeRabbit signal has been dispositioned.
  • changeset-bot release notice — addressed: A valid patch changeset for @mastra/core is present.
  • No human reviews or unresolved inline review threads were present.

Adversarial check

The strongest request-changes case is that delaying the first emitted array chunk changes streaming latency for single-snapshot consumers; it does not survive because preserving the existing one-chunk closed-array contract requires waiting for stream completion, while multi-snapshot consumers begin valid incremental output as soon as the second snapshot establishes that mode.

Assumptions

  • Preserving the existing single-chunk output boundary is deliberate and preferable to always emitting separate opening/content/closing chunks, based on the existing contract and regression tests.
  • Vercel authorization failures are repository deployment-environment limitations, not evidence of a defect in this core-only change.

Open questions

  • None.

@abhiaiyer91
abhiaiyer91 merged commit e8808e3 into mastra-ai:main Aug 14, 2026
6 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

complexity: low Low-complexity PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Array textStream emits invalid JSON when the first object chunk already has elements

2 participants