Plex on odin had 101 of 283 TV shows showing grey placeholder posters. The
cause was not missing images: for every affected show the poster was still
downloaded in the item's metadata bundle, but no candidate was marked
`selected` in the database, so the thumb URL resolved to a 404. Working shows
had exactly one selected candidate; all 101 affected had zero.
Two details make this easy to misdiagnose, so both are captured in the docs:
only 6 items lacked a `thumb` field outright, while 95 advertised a
well-formed thumb URL that 404s on fetch — a metadata-only audit reports the
library as healthy. And a bulk metadata refresh, the obvious suspect, does not
correlate with the damage.
Adds three scripts against the Plex HTTP API (no SSH to odin needed):
plex-health read-only report — server, updates, libraries, unmatched
media, Butler tasks, active streams; exit 0/1/2
poster-audit read-only; fetches every thumb to find the 404s
poster-repair re-selects the locally-cached poster; --dry-run, idempotent,
skips items whose poster already works so manually-chosen
artwork is never replaced
All 283 TV Shows posters now resolve. The health check also surfaced one
unmatched movie, Battlestar Galactica the Mini Series.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
4.1 KiB
TV Shows: 101 missing posters (investigated 2026-08-15)
What the symptom looked like, what it actually was, and what is still unknown.
Symptom
Grey placeholder posters scattered through the TV Shows library in the Plex UI.
Scope
| Library | Type | Affected |
|---|---|---|
| TV Shows (id 2) | shows | 101 of 283 (36%) |
| TV Shows (id 2) | seasons | 0 of 954 |
| Movies (id 1) | movies | 0 (sampled 60 of 1059) |
| Movies 4k (id 3) | movies | 0 (sampled 40 of 79) |
Only show-level posters, only in one library. Seasons and episodes were untouched, which is why the library still looked half-normal.
The two failure modes
They are indistinguishable in the UI, and only one is visible to a metadata-only audit:
| Mode | Count | What it looks like in the API |
|---|---|---|
MISSING |
6 | the item has no thumb field at all |
BROKEN |
95 | the item has a thumb field, but fetching it returns 404 |
The 95 BROKEN shows are the trap. /library/sections/2/all reports a perfectly
well-formed thumb URL for each one — e.g. /library/metadata/13343/thumb/1785398727 —
and only an actual HTTP request reveals it serves an 85-byte HTML 404 page.
Root cause: a lost selection pointer, not a lost file
For every affected show, /library/metadata/<key>/posters still returned a full
candidate list — 85 to 158 entries — including the agent's own poster already
downloaded into the item's metadata bundle as a metadata://posters/... entry.
What was gone was the selection:
affected shows with zero `selected: true` candidates: 101 / 101
control sample of working shows, selected count: 1 each (15 / 15)
affected shows with the poster still in the bundle: 100 / 101
So the images were never deleted. The database simply no longer marked any candidate as selected for those items, and a show with nothing selected has no poster to serve.
Because the file was still local, the repair was a pure database operation — instant, no internet round trip, no re-download:
PUT /library/metadata/<key>/poster?url=metadata://posters/tv.plex.agents.series_<hash>
Validated on one show (Severance, 13343) before touching the rest: thumb went from
404 to 200 image/jpeg, 533 KB.
Hypotheses tested and rejected
- Unmatched media. Rejected: all 283 shows had real
plex://show/...GUIDs; none had alocal://GUID. The affected shows also had full season and episode counts. - A bad bulk metadata refresh. A refresh clearly did run — 77 shows share an
updatedAtinside a 35-second window (~2026-07-30). But it does not correlate with the damage: of the items in that window 54 are fine and 23 are broken, while 72 broken items sit outside it entirely. Rejected as the cause. - A stale/expired thumb URL format. Rejected: broken and working items had
structurally identical
thumbURLs with timestamps from the same refresh.
Still unknown
Which event cleared the selection flags. Establishing that needs the Plex server logs
on odin (Plex Media Server.log), which requires filesystem access to the container's
appdata — not available from dev at the time of writing (no SSH key is authorized on
odin). The CleanOldBundles Butler task is enabled on a 7-day interval and is the obvious
suspect to check first, but note that it does not fit the evidence cleanly: the poster
files survived in the bundles: only the database pointers were lost.
Worth watching: if posters rot again on roughly a 7-day cadence, that points at a Butler task; if it correlates with a Plex version bump, it points at a migration.
Repair record
| Step | Result |
|---|---|
| Validation on Severance | poster restored, verified 200 image/jpeg |
| Bulk repair, local candidates | 100 shows restored |
| True Detective (no local candidate) | restored via --allow-remote from the agent |
| Final audit | 283 / 283 posters resolve |
Tooling built from this: ../bin/poster-audit and ../bin/poster-repair. Re-run the audit if the symptom returns — it is read-only and will say in a couple of minutes whether it is the same failure.