Look at the error first
Image generation in one of my products started failing end to end. No images, and worse, no useful information about why. The temptation in that moment is to start theorising: maybe the new image model is not supported on that endpoint, maybe the request format is wrong, maybe it is a version mismatch.
The error was right there, and we could not see it
The reason we were theorising is that we could not see the actual error. A defensive error handler, the kind you add so one bad image does not crash a whole batch, was catching the real message, storing it for later, and never logging it. So the logs showed a generic “upstream error” over and over, with no hint of the cause.
The moment we made that handler log the actual message, the next attempt told us everything in one line: the account had hit its billing limit. We had run out of credit. That was the whole bug. Not a code problem at all, an operational one, and we had been about to spend a deploy cycle chasing imaginary technical causes.
Two lessons, both unglamorous
First, opaque errors from an outside service usually have boring causes. Before you speculate about compatibility and formats, look at the actual error message. Speculation feels like progress and is usually slower than reading one line.
Second, and this is the deeper one: every error handler that catches and stores an error should also log it. The handler here was doing something sensible, keeping one failure from sinking the batch. But it stored the message without logging it, so for months nothing went wrong and the gap was invisible. The first time something did go wrong, debugging was far harder than it needed to be. The cost of adding the log is negligible. The cost of not having it is paid exactly once, on the day you can least afford it.
Add the log eagerly. Look at the error first.