Repository navigation
v0.0.5 — PremiereDate broke every edit on any item that has one
Fixes editing any item that has a PremiereDate — which, measured against the reporting
library, is 5,916 of 6,583 items: 470 of 502 albums and 5,437 of 5,442 songs.
The report was Internal error. Reference abb6b550a3d3 while editing an artist from the third
page. It was not the page, and it was not the artist as such — that artist was one of only 9 of
639 with a PremiereDate set. Artists are the one kind where this mostly works, which is why
clicking one was the least likely way to find it.
TypeError: Object of type datetime is not JSON serializable
One wrong representation, three boundaries
NormalizedItem.fields held PremiereDate as a datetime; only fields present in changes
were converted to an ISO string on the way out. Fixing each destination separately is what
turned one bug into three reports:
| # | Boundary | Symptom |
|---|---|---|
| 1 | snapshot → JSONB column | the reported failure — after review and confirmation, before any write |
| 2 | write payload → httpx json= |
invisible until (1) was fixed, because (1) always failed first |
| 3 | error body → JSONResponse |
fixed earlier, in v0.0.4 |
The fix is at the representation, not the exits: _normalize, the single door into fields,
now emits the wire form for every field. "Nothing in fields is unstorable or unsendable" is an
invariant now, rather than three conversions that each have to be remembered.
Why this was worse than a 500
The failure landed between the confirm button and the write. A change set would be reviewed,
approved, and then fail with an internal error — with no snapshot written to explain what had
been about to happen. The guardrails held (nothing was half-written), but the error gave no clue
that the date was the problem.
Also fixed
An unanticipated failure on one item killed the whole batch. apply_job caught only
MetaeditError, so anything else escaped with the session still dirty, the next item's flush
raised PendingRollbackError, and the run stopped with the real cause buried under a second
error. Items are committed independently so one failure does not discard the rest; that
guarantee now covers failures we did not predict, not just the ones we did.
What to know before upgrading
Jellyfin rounds PremiereDate to midnight on every write. Verified on 12.2.0: writing back
a value the item already held returns midnight instead. So editing any field on a dated item
rounds its date. This is Jellyfin's behaviour, not this release's — but until now you could not
reach a write on a dated item at all, so this release is when you can first meet it.
Six artists in the reference library are affected, where the time component is a timezone offset
and the date itself is unchanged. It is not worked around: the endpoint is a full overwrite, so
omitting the field would null it, and refusing every edit to a dated item would make most of the
library uneditable. Recorded in ADR 0014 because the alternative is finding it in your own library.
GET /items/{id}/state returns PremiereDate as an ISO string rather than a datetime. The
old shape was a latent 500 for any handler that echoed it; the new one is the shape that always
worked. The published contract is unchanged.
Verification
| Check | Result |
|---|---|
| Backend tests | 799 passed (20 deselected) |
| End-to-end (Playwright) | 27 passed |
| Contract | the committed contract matches the app |
| Frontend unit | # pass 17 |
| Lint / format / mypy | clean |
| Live apply and revert | artist, album, song — all three kinds |
Every new test was confirmed to fail against the old code by reverting the fix. The snapshot
tests assert the property that matters — the stored body and the sent payload must both survive
encoding — rather than the shape of the fix.
Not covered by CI: the 27 e2e specs run locally only. Given that this release's headline bug
was reachable from the browser, that is worth knowing when judging "CI green".
Upgrading
docker compose build && docker compose up -dNo migration. The PremiereDate change is internal to fields; existing snapshot rows read
back unchanged, and the reference library's 658 prior snapshots were verified to still load.