Code Beautifier
Expected number, received string

Fix Zod "Expected number, received string"

Zod's type mismatch when a value arrives as "42" but the schema says z.number(). Why the sample set the type, and when to use z.coerce.number() instead.

Input that triggers it

{"page": "2", "limit": 25}
Open JSON to Zod on its own page
Draft saved locally.

Local workspace

Named projects in IndexedDB · Local only — never synced to our servers. Worksp

Open manager

Batch workspace

Format multiple files locally in one run.

json
Zod schematypescript

What the error means

Zod validates a value against a schema and reports each mismatch with a path and a message. Expected number, received string (Zod 3) or Invalid input: expected number, received string (Zod 4) means the schema declared z.number() at that path and the input carried a string. No coercion is attempted by default: "2" is a string, and z.number() rejects it, exactly as TypeScript would reject assigning a string to a number.

In the example, page arrives as "2". A schema generated from a sample where page was 2 declares z.number() and fails on this input.

Why it happens

  • The value came from a URL or a form. Query parameters, route parameters, and form fields are always strings, however numeric they look. This is the most common source.
  • The API sends numbers as strings for IDs or money, to avoid precision loss in JavaScript clients.
  • The generation sample had a number where the real input has a string.
  • Environment variables, which are strings, validated against a schema that expects numbers.

How to fix it

  1. Decide what the value is. If it is a quantity you will do arithmetic on and it merely arrives as a string, coerce it: z.coerce.number() converts "2" to 2 and still rejects "abc". This is the right tool for query strings and forms.
  2. If it is an identifier — an order number, a SKU — z.string() is the honest schema. Paste a sample where the value is a string into the generator above and it emits z.string().
  3. If the producer is yours and the value should be a number on the wire, fix the producer; coercion at the consumer hides the inconsistency.
  4. For a field that is legitimately either, z.union([z.number(), z.string()]), which the generator emits when samples disagree.

The schema for the example, with coercion where it belongs:

import { z } from "zod";

export const RootSchema = z.object({
  page: z.coerce.number().int(),
  limit: z.number().int(),
});

If it still fails

  • Read the path in the issue: ["items", 3, "price"] means the third element of items, and the coercion belongs on the element schema.
  • z.coerce.number() turns "" into 0 and null into 0, which may not be what you want; add .min(1) or validate emptiness first.
  • The same mismatch in Pydantic reads Input should be a valid integer and in Go cannot unmarshal string into Go struct field; JSON to TypeScript shows the permissive typing of the same data.

Related errors