Skip to content

Conversation

NicolasPllr1
Copy link

@NicolasPllr1 NicolasPllr1 commented Sep 22, 2025

Change Summary

This changes the nullable validator. When the inner validation fails, it now adds a NoneRequired error-line.

The first commit updates the nullable validator, while follow-up commits update test data. These test data become more verbose especially with recursive models. This is similar to what happens with union types when the validation fails, you get lots of error-lines.

I am not sure this is the 'correct' thing to do with optional types, but it's the simplest way I found to address pydantic#8852 and is inspired by the proposed solution there.

Note: maybe documentation could be updated following such a change. I decided to wait for feedback before proceeding further!

Example

The goal was to have this behavior on optional fields:

from typing import Optional
from pydantic import BaseModel

class User(BaseModel):
    age: Optional[int] # or an explicit union, e.g. int | None

User(age="hello")
pydantic_core._pydantic_core.ValidationError: 2 validation errors for User
age
  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='hello', input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/int_parsing
age
  Input should be None [type=none_required, input_value='hello', input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/none_required

With this change, the errors one gets from an optional type - i.e. a type where None is an accepted value - look like the errors one gets when working with union types.

For example, if we go int | bool instead of int | None:

class User(BaseModel):
    # age: Optional[int]  # or an explicit union, e.g. int | None
    age: bool | int

User(age="hello")
pydantic_core._pydantic_core.ValidationError: 2 validation errors for User
age.bool
  Input should be a valid boolean, unable to interpret input [type=bool_parsing, input_value='hello', input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/bool_parsing
age.int
  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='hello', input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/int_parsing

Both errors - with the union type and with the optional type - have the exact same shape now.

Related issue number

Checklist

  • Unit tests for the changes exist (in the sense that existing unit tests cover nullable fields and I updated them)
  • Documentation reflects the changes where applicable
  • Pydantic tests pass with this pydantic-core (except for expected changes)
  • My PR is ready to review, please add a comment including the phrase "please review" to assign reviewers

Selected Reviewer: @Viicos

- 1 new 'none-required' error-line per x_i -> 100 new lines
- 2 new error-line for sub-branch

So 102 new lines in total.
Copy link

codecov bot commented Sep 22, 2025

Codecov Report

❌ Patch coverage is 0% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/validators/nullable.rs 0.00% 7 Missing ⚠️

📢 Thoughts on this report? Let us know!

@NicolasPllr1 NicolasPllr1 changed the title Add none required error on optional fields Add NoneRequired error on optional fields Sep 22, 2025
Copy link

codspeed-hq bot commented Sep 22, 2025

CodSpeed Performance Report

Merging #1791 will not alter performance

Comparing NicolasPllr1:add-none-required-error-on-optional-fields (91b130b) with main (0cd11fe)

Summary

✅ 163 untouched

@NicolasPllr1 NicolasPllr1 marked this pull request as ready for review September 22, 2025 11:49
@NicolasPllr1
Copy link
Author

please review

@samuelcolvin
Copy link
Member

Thanks for the contribution.

Surely this is going to be a breaking change for lots of people?

I think if we were to accept this, it would need to be behind a config flag, and opt in.

@NicolasPllr1
Copy link
Author

Thanks for the quick reply!

Surely this is going to be a breaking change for lots of people?

I did not think of that ... If their program relies on the specific errors emitted / the number of errors, then yes it's a breaking change.

I think if we were to accept this, it would need to be behind a config flag, and opt in.

Happy to work on that if you think this makes sense / had value.

@davidhewitt
Copy link
Contributor

As per the versioning policy https://docs.pydantic.dev/latest/version-policy/#pydantic-v2 I think adding additional error detail is permitted without considering it breaking.

I'd be quite up for having this. Consider e.g. a three-member union:

from pydantic import BaseModel

class User(BaseModel):
    age: int | float | None

user = User(age='abc')
ValidationError: 2 validation errors for User
age.int
  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='abc', input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/int_parsing
age.float
  Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='abc', input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/float_parsing

It feels like we should have an age.None member there!

@Viicos
Copy link
Member

Viicos commented Sep 22, 2025

I'd also be up to have this included without consider it breaking. Alternatively this could make it for V3, but I'm not sure introducing an opt-in mechanism under V2 is worth it (I don't users will ever enable it, it's a welcomed improvement but not a necessity for end users).

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

Successfully merging this pull request may close these issues.

4 participants