
When the cover won't load — the fallback that fixed it
The symptom
Some things you only notice when the user complains. In my case, the user was me: I browsed to the blog after an automatic publication and saw a card with a dark blob where the cover should be. The post was live, the text perfect, the link working — but the cover looked like it had vanished.
It hadn’t vanished. It was there, just too dark to look like an image.
The investigation
My first instinct was to blame the upload. Missing file? Wrong path in the frontmatter? Neither: the file existed, the path was correct, HTTP returned 200. The problem was something else: the cover had been generated by a path that produces a small, dark image, with almost the whole space filled by a near-black blue.
That’s when I found the root: the automatic pipeline had a step using PIL (programmatic drawing) as its default path — not as a fallback. When the main image service was slow, it dropped into that path and published anyway. Nobody checked whether the cover was actually a cover.
The two-layer solution
Instead of patching the symptom, I fixed the whole chain:
Layer 1 — generation with a real fallback. The cover step now requires an AI-generated image, with an explicit fallback to a second generation provider (via NVIDIA API). If the first fails, the second takes over. The programmatic drawing was demoted to what it always should have been: a last resort, used only for old posts.
Layer 2 — a silent watchdog. A script checks the most recent posts twice a day. If a cover is too small (the classic sign of a bad image), it regenerates by itself, commits, publishes, and only then notifies. When everything is fine, it stays quiet — silence is the OK status.
The fire test
I wasn’t going to trust it without proof. I took a good cover, swapped in a bad version in the repository, and waited for the watchdog to run. It detected it, regenerated through the fallback chain, committed, and published. A 22KB cover became one of almost 500KB. Then I restored the original and the system went back to peace.
The lessons
- Size is a cheap and effective proxy. A good AI cover weighs hundreds of kilobytes; a dark programmatic image weighs tens. You don’t need computer vision to detect the problem — bytes are enough.
- Fallback is a design decision, not an accident. Letting the pipeline “choose” the easiest path without criteria is a recipe for silently shipping the worst possible result.
- Good automation is quiet. The watchdog doesn’t spam the chat when everything works. It only shows up when it did something or when something truly broke.
- Fire tests are worth gold. Simulating the failure before trusting the system is what separates “works on my machine” from “works when nobody is watching”.
The blog keeps publishing twice a day, and I keep not looking over its shoulder.