Skip to content

Build Not yet confirmed elsewhere1 publisher 2 min read Published

Zod 4.6.7 omits every .refine() from the JSON Schema it hands to structured-output models

Zod 4.6.7's JSON Schema converter has 13 check keys and none is .refine(), according to a dev.to reading of its source. The post says the OpenAI Node SDK and Vercel AI SDK both use it, so custom rules run only after parsing.

The Engineer · Build desk

Send a correction

Zod drops .refine() before the model sees it Which SDKs hand Zod 4 schemas to models, and which rule types the converter skips, per a dev.to post reading Zod's source.

The OpenAI Node SDK and Vercel AI SDK pass Zod 4 schemas to models via z.toJSONSchema(), Vercel in io: "input" mode. .refine(), object .refine() and .superRefine() become custom checks the converter skips, with no warning. A string transform is shown as "string"; Number() can yield NaN.

Zod drops .refine() before the model sees it
WhoHowKindClaim
OpenAI Node SDKCalls z.toJSONSchema() on Zod 4 schemas; that JSON is what constrains the modelexposure4
Vercel AI SDKUses the io: "input" mode when converting schemasdecision13
Structured-output helpersPassing a Zod 4 schema drops every .refine() before the model sees it, with no warningexposure6
.refine() rulesStored as a check named custom, which is not a contributors key, so the rule is skippedconstraint3
Object .refine(), .superRefine()Both also land as custom checksconstraint15
String transformsInput mode tells the model only "string"; Number() can return NaN with no validation errorexposure12

What happened

  • Zod stores every .refine() as a check named custom in api.ts, and that name is not among the keys the converter's contributors map handles.
  • In the post's refund schema, regex, positive, enum and ISO date rules have JSON Schema keywords, while the note refine, the object-level date refine and the sku transform do not.
  • In input mode Zod's docs say additionalProperties is not set on objects, and the Vercel AI SDK adds it back with its own addAdditionalPropertiesToJsonSchema pass.
  • For z.string().transform(Number), input mode tells the model only "string", and Number() can return NaN with no validation error, according to the post.

Why it matters

  • Who's exposed exposure A Zod 4 schema with .refine(), .superRefine() or an object-level check gives the model a weaker contract than the parser enforces.
  • What it costs cost A broken custom rule surfaces only after the model has answered, so the first signal is a failed parse on a response already generated and paid for.
  • Who has to decide decision Each rule now needs a placement: a keyword-expressible check, the prompt text, or the parser alone. We'd restate any rule the output depends on in the prompt, because the schema will not carry it.

The silence comes from one line. In v4.6.7, the release of 12 October 2026, the converter loops over a schema's checks and runs `contributors[ch._zod.def.check]?.(agg, ch._zod.def)` for each [1]. The map holds 13 keys, among them greater_than, less_than, multiple_of, min_length and string_format [2]. Written as an optional call, a lookup that misses does nothing and raises nothing [1]. That matches the post's description of a drop with no warning [6].

Take the refund schema in the post. Its object-level refine requires refundDate to be on or after orderDate [14]. That refine is stored as a custom check too, so the schema the model receives describes two ISO date strings and says nothing about their order [17].

The evidence has limits. The code reading comes from a single dev.to post, and its JSON output is a reconstruction. The author wrote, "I haven't run this exact schema myself." [16] Each property was built from the converter's test snapshots and processor source, with the long date regex and the $schema line trimmed [8].

The strongest evidence in the post is a snapshot in to-json-schema.test.ts. The test puts two refined string schemas in a union, one requiring a length above 2 and the other a length below 9, and the result collapses to a bare "type": "string" [9]. The post reasons that the two branches can dedupe only if neither refinement reached the output [10].

For the finding to hold in your stack, your Zod version's map must still lack a custom entry, and your SDK version must route Zod 4 schemas through z.toJSONSchema() [4]. The post reads v4.6.7 only [1]. It gives a one-line check: console.log(JSON.stringify(z.toJSONSchema(Refund, { io: "input" }), null, 2)) [8]. That call uses the input mode, which the post says the Vercel AI SDK uses [13].

What to watch

  • Whether Zod adds a custom entry or a warning for skipped checks in a release after v4.6.7.
  • Whether the OpenAI Node SDK or Vercel AI SDK reports dropped checks when it post-processes the schema.
  • A schema printed from a real install, since the post's JSON output is a reconstruction.

Clarity's read

What the record supports and how the coverage leans. The claims behind it follow.

Reality

Evidence62
Adoption
Insufficient
Hype gap+8
Incentives25
Confidence58
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [1]

    In Zod v4.6.7, released 12 October 2026, json-schema-processors.ts loops over each check and runs contributors[ch._zod.def.check]?.(agg, ch._zod.def) to write validation rules into the schema.

    Reported Supported Source: dev.to post reading Zod's sourceView cited source
  2. [2]

    The contributors map has 13 keys, including greater_than, less_than, multiple_of, min_length and string_format.

    Reported Supported Source: dev.to post reading Zod's sourceView cited source
  3. [3]

    Every .refine() is stored as a check named custom (api.ts), and custom is not one of the contributors keys, so the rule is skipped.

    Reported Supported Source: dev.to post reading Zod's sourceView cited source

Get this ledger inside ChatGPT. Ask about any of our stories and the answer brings its figure, each claim’s verdict and the sources. Free, no account.

Add to ChatGPTAdd to Claude

Sources

1 independent publisher whose own reporting we read for this story.

  1. dev.to

    1 article · October 15, 2026

    Your Zod .refine() Never Reaches the Model: What Survives the Trip to JSON Schema

Share your take

Let Clarity write the post for you.

Signed-in readers get a short post drafted on this story in the register they choose — narrative, analytical, or a direct position — editable to the last word before it goes anywhere. The share buttons at the top of this story work without an account.

Topics and entities

Follow any of these and your For You feed starts watching them — no settings page required.

Topics

Loading related stories