Skip to content

fix(schema-compat): escape discriminator key in generated discriminatedUnion#18623

Open
greymoth-jp wants to merge 1 commit into
mastra-ai:mainfrom
greymoth-jp:fix/schema-compat-escape-discriminator-key
Open

fix(schema-compat): escape discriminator key in generated discriminatedUnion#18623
greymoth-jp wants to merge 1 commit into
mastra-ai:mainfrom
greymoth-jp:fix/schema-compat-escape-discriminator-key

Conversation

@greymoth-jp

@greymoth-jp greymoth-jp commented Jun 29, 2026

Copy link
Copy Markdown

Problem

@mastra/schema-compat's jsonSchemaToZod builds a Zod schema as a source string that is later evaluated at runtime with Function(). When it detects a discriminated union (an anyOf of objects that share a const property), it inserts the discriminator property name into the generated z.discriminatedUnion(...) call directly:

parsed = `z.discriminatedUnion("${discriminator}", [${schema.anyOf...}])`;

Every other string this generator emits is serialized with JSON.stringify (object keys, .describe(...), .default(...)), but the discriminator key is interpolated raw. A JSON Schema property name may legally contain a double quote, so a name such as ty"pe produces:

z.discriminatedUnion("ty"pe", [z.object({ "ty\"pe": z.literal("cat"), ... }), ...])

The object key is escaped correctly ("ty\"pe"), but the discriminatedUnion argument is not, so the generated string throws SyntaxError: missing ) after argument list when it is evaluated. This is the same kind of runtime Function() failure that was fixed for parseOneOf in #11610.

Solution

Serialize the discriminator key with JSON.stringify, the same helper already used for the object keys it refers to:

parsed = `z.discriminatedUnion(${JSON.stringify(discriminator)}, [${schema.anyOf...}])`;

For any ordinary property name this is byte-identical to the previous output, since JSON.stringify("type") === '"type"', so existing schemas generate exactly the same code.

Verification

I added a test in the DiscriminatedUnion Detection block that uses a discriminator name containing a double quote, evaluates the generated string with Function() the way the output is consumed, and asserts it no longer throws and parses a matching value.

  • Before the change the new test fails: the generated code throws a SyntaxError when evaluated.
  • After the change it passes.
  • The existing discriminated-union tests are unaffected, because the generated code for their normal property names is identical.

ELI5

This change fixes a bug where a special character in a schema property name could break generated code. Now the property name is safely escaped before being put into the code, so schemas with quotes in their discriminator key work correctly.

Changes

  • Fixed jsonSchemaToZod to serialize discriminated-union keys with JSON.stringify, matching other generated strings.
  • Added a test that uses a discriminator name containing a double quote and verifies the generated code runs and parses successfully.
  • Added a changeset entry documenting the patch release for @mastra/schema-compat.

Why

  • Prevents runtime SyntaxError when JSON Schema discriminator keys contain double quotes.
  • Keeps normal output unchanged for standard property names.

@changeset-bot

changeset-bot Bot commented Jun 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8d6f680

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

This PR includes changesets to release 29 packages
Name Type
@mastra/schema-compat Patch
mastracode Patch
@mastra/agent-builder Patch
@mastra/core Patch
@mastra/editor Patch
@mastra/memory Patch
@internal/playground Patch
@mastra/server Patch
@mastra/voice-google-gemini-live Patch
@mastra/voice-openai-realtime Patch
@mastra/voice-xai-realtime Patch
@mastra/client-js Patch
@mastra/mcp-docs-server Patch
@mastra/opencode Patch
@mastra/longmemeval Patch
@mastra/express Patch
@mastra/fastify Patch
@mastra/hono Patch
@mastra/koa Patch
@mastra/nestjs Patch
mastra Patch
@mastra/deployer-cloud Patch
@mastra/react Patch
@mastra/playground-ui Patch
@mastra/deployer Patch
create-mastra 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 Jun 29, 2026

Copy link
Copy Markdown

@greymoth-jp 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 needs-issue PR is missing a linked issue label Jun 29, 2026
@coderabbitai

coderabbitai Bot commented Jun 29, 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: CHILL

Plan: Pro

Run ID: 9d8c002f-ef34-448e-85e5-9b84f73ae190

📥 Commits

Reviewing files that changed from the base of the PR and between e62c108 and 8d6f680.

📒 Files selected for processing (3)
  • .changeset/escape-discriminated-union-key.md
  • packages/schema-compat/src/json-to-zod.test.ts
  • packages/schema-compat/src/json-to-zod.ts

Walkthrough

In json-to-zod.ts, the discriminator argument in z.discriminatedUnion(...) generation is changed from a template string to JSON.stringify(discriminator). A test is added verifying correct escaping and runtime evaluation when the discriminator property name contains a double quote. A changeset entry documents the patch.

Changes

Discriminator key escaping in z.discriminatedUnion codegen

Layer / File(s) Summary
Fix and test for quoted discriminator key
packages/schema-compat/src/json-to-zod.ts, packages/schema-compat/src/json-to-zod.test.ts, .changeset/escape-discriminated-union-key.md
Discriminator argument construction switches from template string to JSON.stringify(discriminator); new test confirms escaping and runtime validity; changeset documents the patch.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

🚥 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 schema-compat fix for escaping discriminator keys in generated discriminated unions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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.

@dane-ai-mastra

Copy link
Copy Markdown
Contributor

PR triage

This PR needs to fix an existing issue. Please link an issue in the PR description, for example with Fixes #1234 or Closes #1234.

Applied label: needs-issue

PRs without a linked issue will automatically close after 1 day(s) with the label.


Changed test gate

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

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

Labels

needs-issue PR is missing a linked issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant