TEASEDocs
ProductsClubTroubleshooting

When an empty block misreads its own cause

Collapsing four distinct empty reasons into one generic message has caused a real incident — here's what to check before you trust a block's explanation.

Diagnosing an empty block assumes the block's stated reason is correct. Usually it is — the reason comes from a server-sent code, not client-side guesswork — but the code is only as good as the check that produced it, and that check can point at the wrong table.

Why a single generic message is risky

A block that always explains emptiness the same way — "the engine hasn't collected this data yet" — is making two claims at once: it will pass on its own, and the cause is missing collection. If the real cause is something else (the platform doesn't send this metric at all, the block is reading past a window with genuinely zero rows, or the account was never connected for this source), a generic message is now actively wrong, not just vague — it sends you to "wait for collection" when there's nothing to wait for.

A wrong explanation is worse than no explanation

This is exactly why dashboard_empty.py ties each block's empty state to one of four distinct, machine-checked gap codes (see Diagnose an empty dashboard block) instead of one reused phrase: the message is only as trustworthy as the check that produced it.

What changed

The generic sentence is gone. Every empty state now names one of four specific, provable causes — see the four codes — and a code is only used when the fact behind it has actually been checked for that specific block, against that specific data source.

Fansly has a richer internal taxonomy than the four public codes

Under the hood, Fansly's dashboard classifies an empty block by more than four internal reasons — whether an endpoint is even confirmed to exist on the wire, whether it's confirmed but has produced no rows, whether the numbers are capped by a row limit, whether several currencies in the window can't be summed, or whether a tracking-link total is lifetime and the period simply doesn't apply to it. Only some of those get folded into the four public codes you see on the block; a few (a row-limit cap, a mixed-currency window, a lifetime-only metric) stay as their own plain-text caveat with no code and no "Why?" link at all — because there's no doc page written for a caveat that isn't really an empty state, just an asterisk on real numbers.

If a block shows numbers and a caveat sentence with no "Why?" button, that's working as intended — don't go looking for a missing docs page for it.

The Why? button on an empty block links to one URL per code, built once from the code name — it isn't reading a page from wherever this doc happens to live today. If this page (or its sibling code pages) ever moves to a different path, the in-product link and the page a workspace owner actually lands on can drift apart. That's not something you can fix from inside the panel — it's a docs-publishing detail worth checking whenever this section of the site gets reorganized, not something to diagnose as a product bug.

What's next

On this page