Skip to content
GitHub
Build Errors

Ruff TC003 unsafe-fix breaks Pydantic models using uuid with PEP 563

Ruff TC003 Breaks Pydantic Models Using uuid with PEP 563

Severity: P2 (auto-fix corrupts working Pydantic models silently) · Applies to: any package using from __future__ import annotations + Pydantic + uuid.UUID fields with ruff check --unsafe-fixes Related: ADR-012 — Dev tooling (Biome, git-cliff, tiered hooks, ruff TD rules)

Problem

After running ruff check --unsafe-fixes --fix, Pydantic models that use uuid.UUID fields fail at runtime with:

pydantic.errors.PydanticUserError: `ModelName` is not fully defined;
you should define `uuid`, then call `ModelName.model_rebuild()`.

Symptoms:

  • Tests that instantiate Pydantic models fail after ruff auto-fix
  • Only affects files with from __future__ import annotations (PEP 563)
  • Only affects files where uuid is used in Pydantic BaseModel fields

Root Cause

Ruff rule TC003 (“Move standard library import into a type-checking block”) moves import uuid into an if TYPE_CHECKING: block. This is normally safe for pure type hints, but breaks Pydantic models when PEP 563 is active.

The interaction:

  1. from __future__ import annotations makes all annotations strings (PEP 563)
  2. Pydantic evaluates annotation strings at model creation time to build validators
  3. When uuid is behind TYPE_CHECKING, it’s not available at runtime
  4. Pydantic can’t resolve uuid.UUID and raises class-not-fully-defined
# BREAKS — uuid not available at runtime for Pydantic
from __future__ import annotations
from typing import TYPE_CHECKING
from pydantic import BaseModel
if TYPE_CHECKING:
import uuid
class MyModel(BaseModel):
id: uuid.UUID # Pydantic can't resolve this at runtime

Solution

  1. Keep import uuid as a runtime import (not behind TYPE_CHECKING)
  2. Add TC003 to ruff’s ignore list in pyproject.toml:
[tool.ruff.lint]
ignore = [
"TC001", # FastAPI needs Pydantic models at runtime, not behind TYPE_CHECKING
"TC003", # stdlib uuid used in Pydantic models — must stay at runtime with PEP 563
]

Correct Pattern

from __future__ import annotations
import uuid # Runtime import — Pydantic needs this
from pydantic import BaseModel
class MyModel(BaseModel):
id: uuid.UUID # Works: uuid is available at runtime

Prevention

Best Practices

  • Never run ruff --unsafe-fixes without running tests afterward — unsafe fixes can change runtime behavior
  • Keep TC003 in the ruff ignore list — this project uses PEP 563 + Pydantic throughout, making TC003 broadly unsafe
  • TC001 is already ignored for the same reason — Pydantic models, FastAPI dependencies, and other runtime-evaluated annotations require their imports at runtime

Warning Signs

  • Ruff reports TC003 violations on files containing Pydantic models
  • --unsafe-fixes flag is used without subsequent test verification
  • New domain entity files that use from __future__ import annotations

References