Third report on the free AI Visibility Checker in one afternoon, and the first two fixes were right about their own layer and blind to the one underneath. This entry records the whole path, because fixing it one layer at a time is what made it take an afternoon.
What the visitor saw: "Network error — please try again."
What actually happened: the request succeeded, the server answered, and the answer was HTTP 502 with a JSON body. Two independent defects turned that into the least informative sentence available.
Defect 1 — our own code returned 502. callClaude mapped "the AI provider answered with an error" to HTTP 502, which is also the status a serverless platform returns when the function itself dies. In Vercel's request log the two are identical: 502, message empty. Diagnosing this meant proving from the source that the 502 was ours — after first testing, and disproving by measurement, two hypotheses about a crashed process (an unhandled 'error' event on a truncated response stream, and a module-level throw). Provider failures now answer 503 with code: "provider", and core-web-vitals.ts loses the same false 502 in three places. 502 is now a status only the platform can produce, which is what makes it worth reading.
Defect 2 — the provider's answer was thrown away. The branch that handled a non-OK provider response discarded both the status and the body, so the only trace of a failed check anywhere in the system was that empty 502. It now logs provider_status and a truncated provider_body — safe, because the key travels in a header that is never echoed back, and asserted by a test.
Defect 3 — eighteen clients could not read an error. Every tool client did const data = await res.json() unguarded. res.json() throws on any body that is not JSON, and a non-JSON body is exactly what arrives when something fails upstream of our handler: a CDN error page, a platform 502, an empty response. So the one case where the visitor most needed a true sentence was the case that fell into catch and said "Network error". The tracked funnel event recorded status 0, so the analytics were wrong too. link-preview-client.tsx had already been fixed on its own, with a comment naming the CDN case, and the fix never spread.
Fixed across 48 parse sites in 40 files — public tools, account and admin panels alike — through one toolError() helper in src/lib/tools/read-response.ts, with a directory-scanning test so the nineteenth client cannot reintroduce it. A genuine network failure still says "Network error", correctly, because no response arrived.