Summary
z.coerce.number().min(0) incorrectly generates nullable: true in OpenAPI schema, while z.coerce.number().min(1) does not.
Reproduction
https://stackblitz.com/edit/vitejs-vite-urf1hjcy?file=src%2Frepro-nullable-bug.ts
const QuerySchema = z.object({
limit: z.coerce.number().int().min(1).default(100), // OK
skip: z.coerce.number().int().min(0).default(100), // BUG: generates nullable: true
});
Output
{
"paths": {
"/test": {
"get": {
"parameters": [
{
"schema": {
"type": "integer",
"minimum": 1,
"default": 100
},
"required": false,
"name": "limit",
"in": "query"
},
{
"schema": {
"type": "integer",
"nullable": true,
"minimum": 0,
"default": 100
},
"required": false,
"name": "skip",
"in": "query"
}
]
}
}
}
}
Expected
Both limit and skip should NOT have nullable: true. The only difference is min(1) vs min(0), which should not affect nullability.
Versions
Tested and confirmed on:
- zod: 3.25.76, 4.3.5
- @asteasolutions/zod-to-openapi: 7.3.4, 8.4.0
Summary
z.coerce.number().min(0)incorrectly generatesnullable: truein OpenAPI schema, whilez.coerce.number().min(1)does not.Reproduction
https://stackblitz.com/edit/vitejs-vite-urf1hjcy?file=src%2Frepro-nullable-bug.ts
Output
{ "paths": { "/test": { "get": { "parameters": [ { "schema": { "type": "integer", "minimum": 1, "default": 100 }, "required": false, "name": "limit", "in": "query" }, { "schema": { "type": "integer", "nullable": true, "minimum": 0, "default": 100 }, "required": false, "name": "skip", "in": "query" } ] } } } }Expected
Both
limitandskipshould NOT havenullable: true. The only difference ismin(1)vsmin(0), which should not affect nullability.Versions
Tested and confirmed on: