fix(api): a non-finite number in a JSON body is refused with a 422, not a 500

The JSON parser under FastAPI accepts `Infinity`/`NaN`, and both progress checkpoints truncate the
position with `int()` — a hand-written checkpoint came back as a 500 with a traceback. Their bodies
now refuse non-finite floats, and the app's validation handler — FastAPI's own shape — echoes a
non-finite input by name: the default one failed to serialise its own error (JSON has no NaN), which
was a 500 by another route. Sibling of the subtitle routes' non-finite query parameters.
This commit is contained in:
2026-09-11 03:18:17 +02:00
parent 473d9f02f2
commit 7360339039
3 changed files with 56 additions and 2 deletions
+34
View File
@@ -0,0 +1,34 @@
"""A progress checkpoint takes a FINITE position.
The JSON parser under FastAPI accepts `Infinity` and `NaN`, and both progress handlers truncate the
position with `int()` — so one hand-written checkpoint came back as a 500 with a traceback. Found in
the 2026-09 bug hunt's second review round, as the sibling of the subtitle routes' non-finite
`at`/`offset`. Pure: the body is refused before the handler touches the database.
"""
from types import SimpleNamespace
import pytest
from fastapi.testclient import TestClient
import app.state as app_state
from app.auth import current_user
from app.main import app
@pytest.fixture
def client(monkeypatch, override_dep) -> TestClient:
override_dep(current_user, lambda: SimpleNamespace(id=1))
monkeypatch.setattr(app_state, "_configured_cache", True) # pass the instance-setup gate, no DB
return TestClient(app)
@pytest.mark.parametrize("path", ["/api/videos/v1/progress", "/api/plex/item/98614/progress"])
@pytest.mark.parametrize("raw", ["Infinity", "-Infinity", "NaN"])
def test_a_non_finite_position_is_refused(client, path, raw):
r = client.post(
path,
content=f'{{"position_seconds": {raw}, "duration_seconds": 3000}}',
headers={"content-type": "application/json"},
)
assert r.status_code == 422, r.text