diff --git a/graph/fixes/fix-pydantic-bare-default-evaluated-at-class-definition-time-530bfb.md b/graph/fixes/fix-pydantic-bare-default-evaluated-at-class-definition-time-530bfb.md new file mode 100644 index 00000000000..55fefc41f55 --- /dev/null +++ b/graph/fixes/fix-pydantic-bare-default-evaluated-at-class-definition-time-530bfb.md @@ -0,0 +1,38 @@ +--- +id: 530bfb6f-841c-4381-b8a4-d0b6fbc02878 +type: fix +title: "Fix: Pydantic bare default evaluated at class definition time (paper-dynasty-database)" +tags: [paper-dynasty, python, pydantic, fix, bug, fastapi] +importance: 0.65 +confidence: 0.8 +created: "2026-03-03T20:06:06.122074+00:00" +updated: "2026-03-03T20:06:06.122074+00:00" +--- + +## Problem + +`PlayerModel.offense_col` in `app/routers_v2/mlbplayers.py` used a bare `random.randint(1, 3)` as the Pydantic field default. Python evaluates this expression once at class definition time (module import), so every instance shared the same random integer for the entire process lifetime. + +## Root Cause + +Pydantic field defaults that are not wrapped in `Field(default_factory=...)` are evaluated eagerly at class body execution time — identical to how Python function default argument values work. This is a common footgun with mutable or randomised defaults. + +## Solution + +```python +# Before (broken) +offense_col: int = random.randint(1, 3) + +# After (correct) +offense_col: int = pydantic.Field(default_factory=lambda: random.randint(1, 3)) +``` + +`default_factory` is called on each model instantiation, producing a fresh value per request. + +## Files Changed + +- `app/routers_v2/mlbplayers.py` line 40 + +## References + +- Issue #24, PR #38 in cal/paper-dynasty-database