Ten Repairs, One Character
Only after confirming nine alibis did the real culprit appear
StackTube has a feature that bundles a month's worth of notes into a single ebook. At month's end, that month's video notes become chapters, a cover is applied, and it's delivered by email and Kindle. I call it the monthly mook. This is the story of the weekend I built it.
The build succeeded. The file was generated. The file size looked plausible. But when I opened the book, it was blank.
A book with a table of contents but no body. There was no error message. Everywhere I looked in the system, it said "success." A blank book that claimed success. From here, the investigation began.
When there's an error, you send the error message to AI. That's no longer frightening. But this time there was nothing to send. "The book is empty" isn't a clue; it's a symptom. I felt like a patient who goes to the hospital and can only say "it hurts." If you don't know where and how it hurts, the doctor can only start by guessing.
So I started by guessing. Was the font package missing—fixed it. Blank. Was the stylesheet not linked—fixed it. Blank. Was a file omitted in the deploy config—fixed it. Blank. A converter option?—fixed it. Blank. Each time I fixed something, I had the conviction "this time it worked," and each time, the book was empty.
Here was the strange part. Everything I fixed actually was a problem. The font, the stylesheet, the deploy config—all were places that needed fixing. Each one was a real defect. That made it more confusing, because each fix gave me grounds to believe "this must have been the cause." The real culprit was elsewhere, while small-timers kept getting arrested.
This is the trap of this kind of bug. If there's one suspect, the investigation is easy. Examine them and you're done. But if there are ten suspects, and nine of them each committed some different minor offense—singling out the real culprit becomes hard. The more people who've done something wrong, the more it blurs who's actually behind the case.
On the seventh repair, the AI found the culprit. The cause was one line of code. Precisely, a single conversion that turns a string into bytes was missing.
Even after the explanation, I didn't understand for a while. In summary: there's a library that builds each page of the book, and text passed to that library had to be in a specific format. If the format doesn't match, the library can't build that page. But that failure didn't surface—it was replaced by a blank page. So every page was quietly empty.
The fix was .encode("utf-8")—one line. Adding this line produced a 712,841-byte book. A book with a body.
That night I thought: were nine of the ten repairs a waste?
They weren't. That's the biggest lesson I took from this case. The nine repairs weren't waste—they were the process that made the real culprit visible. Once I fixed the font, the stylesheet, the deploy config and erased them from the suspect list, the true cause stood alone in the remaining space. With ten suspects, you can't reason. Confirming nine alibis is tedious, but that's investigation. Without that tedious elimination, you can't point to the last one.
For a non-developer, this was an especially important sense. Since I can't read code, I have no intuition for "this part is probably the cause." A developer might scan the code and immediately narrow down the culprit. I have no such shortcut. What I have instead is elimination. Fix one, erase one, look at what remains. Slow, but it arrives eventually.
And one more thing. I now believe the word "success" a little less. A system saying "build succeeded" and the output being intact are different matters. "Build succeeded" means "there were no errors in the process of making the file," not "the contents of that file are correct." While I mistook the two for the same thing, I made a blank book seven times.
One character. Precisely, one line. But finding that one line required nine eliminations, and those nine were not a waste.
🔧 Technical Terms in This Episode
EPUB The standard ebook file format. Internally, it's multiple web pages (XHTML) compressed and bundled together. Readable on Kindle, Apple Books, and others.
String / Bytes
The characters humans read (strings) and the 0s and 1s machines store (bytes) are different formats. Moving between them requires conversion (encoding). .encode("utf-8") is that conversion.
Elimination A method of reaching the true cause by excluding possible causes one by one. The most powerful tool a person who can't read code has in debugging. If you can't narrow down the cause by intuition, erase one by one and see what remains.
Build Success ≠ Correct Result A successful build means "there were no fatal errors in the making process," not a guarantee that "the contents of the produced output are correct." Distinguishing these two is the core lesson of this episode.
Hotfix An emergency fix to a service in operation. This case had ten hotfixes, and the seventh was the real culprit.