Skip to content
All insights

Sep 23, 20263 min read

Broken and empty are the same pixels

Until 6 August, our client platform could tell a client "Inbox zero, your team is operating cleanly" while the inbox was full. The read behind the screen had failed. The list stayed empty, and an empty list draws the same screen as a clean inbox.

A broken screen and an empty screen can be the same pixels. Nobody reports a bug that looks like good news.

5
client screens that mishandled a failed read, fixed on 6 August

We fixed those five screens that day. Then we went looking for the pattern everywhere else, and kept finding it.

Three ways a failure becomes an empty state

The error message nobody could reach. Four of the five screens already had an error message written. It never appeared. Some code checked for a good response and had no branch for a bad one. Other code parsed a failed response as JSON, which works, because a server error still sends a JSON body. The catch block never ran, the list was set to empty, and the error flag stayed false.

The column that did not exist. Our approval pipeline selected three columns that were never in the database. The database refused the query. The code that read the result treated a refusal as "no rows". So every approval card in the product was quietly dead from the day it shipped until we found it on 28 July. The unit tests passed the whole time, because they replaced the database with a stub that accepts any query you give it.

The column nobody asked for. Our document list requested one AI field. The card that displayed each document read five. A database API does not complain about a column you never requested. It just leaves the field out, and the card's careful defaults filled every gap. On our own template workspace of 683 documents, 379 cards said "No summary yet" beside a summary that existed, and 231 said nobody had read a file directly above the AI's summary of it.

Every line involved was reasonable on its own. Default to zero. Show an empty list when a read fails. Stub the database in a test. Put together, they turn a plumbing bug into a believable empty state.

One variant is worse than a false sentence

One settings screen failed to load a model allowlist and showed nothing selected. Nothing selected means "allow every model", and the Save button stayed live. An admin ticking two boxes would have replaced an allowlist nobody could read.

We fixed that screen so a failed read disables Save. The rule we took from it: a screen that could not read the current value must not be allowed to write a new one.

What stops it now

A failed read has to say it failed. A check in our build fails on any code path that writes an empty value after a failed read without recording the failure. It opened with 43 real cases on 6 August, reached zero the next day, and still reads zero today.

The error branch renders before the empty branch. A failed read leaves the list empty, so whichever branch runs first decides what broken looks like.

Queries are tested against the real schema, not a stub. For the approval pipeline, a test builds the actual column list from our database migrations and fails, by name, on any column a query selects that does not exist. For the document list, a test reads the fields the card uses and fails, by name, on any field the query does not request. We confirmed both tests fail on the code as it was before the fix. A test that cannot fail proves nothing.

What to ask any vendor

If you are buying software your team will trust with real work, three questions get to this fast. What does this screen show when its data fails to load, and can you show me? How do your tests know a query matches the real database rather than a stub? Can a settings screen that failed to read a value still save over it?

Good answers come with a demo. The insights are free. If you want this level of engineering pointed at your operation, start with the free audit. The plan is yours to keep either way.

5x ROI in 30 days. Or we work for free.