This rule raises an issue when SkipValidation is used in an Annotated type that also contains validation constraints like
Field, StringConstraints, or other Pydantic validators.
In Pydantic, SkipValidation converts a field’s validation schema to any_schema, which accepts any value without type or
constraint checks. Combining it with validation constraints in the same type annotation leads to broken behavior regardless of ordering:
SkipValidation is the outermost annotation, it replaces the entire validation schema with any_schema. Any
constraints defined in an inner Annotated block are silently dropped — the field accepts any value, including values that violate the
constraints.SkipValidation is not the outermost annotation, constraints may still be applied as post-validation functions on top of
any_schema. The type check is skipped, so passing a value of the wrong type does not produce a clean ValidationError but
instead causes a TypeError at runtime.This pattern suggests a misunderstanding of how SkipValidation works. Developers likely intended either to validate the data OR to
skip validation entirely, not both. The presence of both indicates a logic error in the code.
For example, in Annotated[Annotated[int, Field(gt=0)], SkipValidation], the Field(gt=0) constraint is completely ignored
because SkipValidation as the outermost annotation converts the whole schema to any_schema, allowing any value to pass
through without validation.
Using SkipValidation alongside validation constraints can lead to security vulnerabilities and data integrity issues:
TypeError instead of a
ValidationError, crashing downstream codeThe severity depends on what the data is used for - user input, configuration data, or data from external systems represent higher risk scenarios.
The fix depends on what was intended:
SkipValidation. All constraints will be properly applied.SkipValidation[Type] on its own. This
is appropriate when the data is already validated upstream (for example, passing a previously-constructed model instance), or when the performance
cost of re-validation is not worth it and the caller guarantees valid data. The type annotation is still present for IDE and type-checker support —
only the runtime validation is skipped.
from typing import Annotated
from pydantic import BaseModel, SkipValidation, Field
class Model(BaseModel):
value: Annotated[Annotated[int, Field(gt=0)], SkipValidation] # Noncompliant - Field(gt=0) is silently ignored
other: Annotated[int, SkipValidation, Field(gt=0)] # Noncompliant - type check is skipped, wrong types cause TypeError
from typing import Annotated
from pydantic import BaseModel, SkipValidation, Field
class Model(BaseModel):
validated: Annotated[int, Field(gt=0)] # constraints properly enforced at runtime
trusted: SkipValidation[int] # validation intentionally skipped; data is already trusted