This is an issue when a Pydantic model field uses Optional[Type] or Type | None type hints without providing an explicit
default value, or when using Field(…) with the ellipsis operator.
In Pydantic models, there is a common misconception about what Optional[Type] means. Many developers assume that marking a field as
Optional[Type] makes it optional during validation, but this is not the case.
The Optional[Type] type hint only indicates that a field can accept either a value of Type or None. It does
not affect whether the field is required during validation. Without an explicit default value, Pydantic will still raise a validation error if the
field is missing from the input data.
To make a field truly optional (meaning it doesn’t need to be provided during validation), you must assign a default value. This is typically
None for optional fields, but can be any appropriate default value.
A particularly problematic pattern is using Field(…) with Optional[Type]. The ellipsis (…) is Pydantic’s
way of explicitly marking a field as required. This creates a direct contradiction: the type hint says the field can be None, but the
Field(…) says it must be provided. In this case, Pydantic prioritizes the ellipsis, making the field required despite the
Optional annotation.
This mismatch between developer intent and actual behavior leads to unexpected validation errors in production, confusing API consumers who receive "field required" errors for fields they reasonably expected to be optional based on the type hints.
When optional fields lack explicit default values, the application will reject valid requests where users omit fields they believe to be optional. This leads to:
Add an explicit default value (typically None) to fields with Optional type hints. This makes the field truly optional
during validation while maintaining the type safety that allows None values.
from typing import Optional
from pydantic import BaseModel
class TwitterAccount(BaseModel):
username: str
followers: int
class UserRead(BaseModel):
name: str
twitter_account: Optional[TwitterAccount] # Noncompliant
from typing import Optional
from pydantic import BaseModel
class TwitterAccount(BaseModel):
username: str
followers: int
class UserRead(BaseModel):
name: str
twitter_account: Optional[TwitterAccount] = None