What the error means
Pydantic v2 validates every field against its annotation and reports each failure with a location, a message, and the offending input. Input should be a valid integer is the message for an int field that received something it could not accept. The full report looks like score: Input should be a valid integer, got a number with a fractional part [type=int_from_float, input_value=18.25], and the type= tag tells you which of several cases you are in.
The example triggers three at once: "id" is a string that could be an integer (int_parsing succeeds in lax mode, so this one only fails in strict mode), "score" is a float with a fractional part (int_from_float), and "rank" is null (int_type).
Why it happens
- A fractional number in an
intfield. Pydantic v2 accepts18.0for an int but rejects18.25; v1 truncated it silently, which is why upgrades surface this. - Null in a non-Optional field.
rank: intrequires an integer;Noneis not one. - A string that is not numeric (
"abc",""), or any string at all understrict=True. - The model was generated from a sample where
scorehappened to be whole andrankhappened to be set.
How to fix it
- Paste a payload that shows the real range of values into the generator above. It emits
floatfor a field with any fractional sample,Optional[int] = Nonefor a field that is ever null, andstrfor a field that is ever a non-numeric string. - In an existing model, change the annotation to match the data:
score: float,rank: Optional[int] = None. - If the producer sends numbers as strings and you want them coerced, leave the field as
intand stay in lax mode (the default);"42"becomes42. Usestrict=Trueonly where coercion would hide bugs. - If a float should be truncated on purpose, say so with a
field_validatorrather than relying on old v1 behaviour.
The model the example produces:
from typing import Optional
from pydantic import BaseModel
class Root(BaseModel):
id: int
score: float
rank: Optional[int] = None
If it still fails
- Read the
type=tag in the error:int_parsing,int_from_float,int_type, andint_too_bigeach point at a different fix. - The same data against a Go struct produces
cannot unmarshal string into Go struct field; the strictness is the same, only the wording differs. - Validate the raw JSON with JSON Validator first if you are unsure whether the value is a number or a string on the wire — the quotes are easy to miss in a log line.
Related errors
Field required
Declare the field with a default (nickname: Optional[str] = None); the generator does this for any key absent from at least one sample.
json: cannot unmarshal string into Go struct field
Make the struct field match the payload (string), or fix the producer to send a number; for numbers-as-strings, tag the field with `json:",string"` or unmarshal into json.Number.