LLM cost accuracy: why we show a dash, not $0.00
A zero is a claim and a missing figure is an absence. For LLM cost accuracy, our dashboard shows a dash wherever a plausible number would have been a lie.
There is a dash on our dashboard where a dollar figure could be, and it is the most argued-over character in the product. A dash looks unfinished. A $0.00 looks professional. Every instinct — design, sales, ego — pushes toward printing the number. Here is why the dash wins, case by concrete case.
Zero is a claim. Null is an absence. They must not share a glyph.
$0.00 asserts: this call cost nothing. When the truth is "we could not determine the cost", printing zero replaces a fact you do not have with a fact that is false — and falsely low, which for a cost product is the worse direction: your dashboard under-reports, the invoice arrives, and the invoice wins. Once a customer catches one confident wrong number, every other number on the screen loses its authority — trust in a metrics product does not degrade gracefully.
So the rule runs the whole depth of the stack, because it only takes one layer to break it. A call on a model with no verified rate gets cost_usd = NULL in the database, never zero. The aggregates must then not launder it: coalesce(sum(cost_usd), 0) quietly turns absences back into zeros, which is exactly the defect we shipped — Postgres skips NULLs in sum(), so unpriced calls simply vanished from totals, and real spend rendered as a confident $0.00. And the UI must render the honest NULL as a dash with a reason, not as an empty cell someone will read as zero anyway.
The cases where a plausible number was available, and refused
Each dash below marks a place where we could print something defensible-sounding.
The unknown model. A model shipped yesterday has no verified rate. The tempting substitutes — a sibling model's rate, a family average, the old rate of the model it replaced — are all guesses wearing precision. We once had a substring fallback that priced every current Opus at a retired Opus's 3x-higher rate; the lesson is tattooed on the rate table now: no default, no sibling, no substring. Dash, plus "we do not have a verified rate for this model".
The write we cannot attribute. Anthropic bills 5-minute cache writes at 1.25x and 1-hour writes at 2x, and sometimes reports the total without the split. When a 1h write was genuinely possible and the breakdown is absent, either assumption is a potential 37.5% error in a known direction. Dash — and a spike in those dashes is itself a signal that the response shape moved where money is at stake.
The saving that does not exist. One provider's cache charges cached tokens at the full input rate — a real cache, zero discount. "$0.00 saved" there would still be a claim about a discount that does not exist. Where the discount is unpublished or zero, the saving is not zero; it is not a thing, and it renders as nothing.
The total with missing rows. When some calls in a window are unpriced, the honest total is not the sum of the priced ones presented as the sum of everything. Below a threshold, the total states its denominator — measured over N of M calls. Past the threshold, the priced remainder stops being a fair sample of the whole, and the total itself becomes a dash. A number that omits rows without saying so is how averages lie.
The four provenance labels
The dash's positive counterpart: every figure that does render carries where it came from. VERIFIED — read off the vendor's published page by a human, on a recorded date. CONTRACT_DESIGNATED — fixed by contractual terms naming an authoritative source (how some Bedrock rates resolve). INFERRED — derived from a published rule, like "cached input is 10% of input": correct by construction, but a derivation, and labelling it VERIFIED would pass a derivation off as a reading. unverified — lowercase on purpose, because it is not a grade of claim but the absence of one, and it never resolves to a number at all.
Two rules give the labels teeth. An aggregate is labelled by its weakest component — one INFERRED row taints the total, because a chain of provenance is as strong as its worst link. And anything irreversible gates on the strong labels only — today that is which savings Vigil will quote at all, and anything that ever bills or blocks on a figure would sit behind the same gate. VERIFIED and INFERRED must also look different on screen; a label that renders identically to a stronger one is the lie relocated into typography.
"Insufficient data" is a feature
The objection writes itself: customers want numbers, dashes look broken. The observed opposite: the dash is why the numbers get believed. A dashboard that visibly refuses to guess, in the cases where guessing would be easy, is demonstrating the property that makes its other numbers worth acting on — every figure it does show survived a pipeline that prefers silence to plausibility. The dash is the evidence. And it is honest in the useful direction: each dash carries its reason — unpriced model, missing breakdown, unpublished discount — so it is not a shrug but a pointer at exactly what is unknown and what would make it known. The public token cost calculator follows the same rule: no verified rate, a dash. The same discipline in the pricing behind the product, and in how the FAQ answers accuracy questions, is one rule applied everywhere: a number you cannot stand behind is not a number, whatever it looks like.
What to do
Find the NULLs in your own metrics pipeline and trace what each becomes by the time it reaches a screen — every coalesce(x, 0), every ?? 0, every chart library defaulting missing points to zero is a place absence becomes claim. Then pick your most-trusted dashboard total and ask what it does when an input row cannot be computed: silently omit, silently zero, or say so. If you cannot answer from the code, the answer is one of the first two.