An agent reports that revenue was £2.4m last quarter. The real figure is £2.9m. Someone calls it a hallucination, the team adds a line to the prompt telling the model to be careful with numbers, and everyone moves on.
Trace it properly and you almost never find a model inventing a number. You find that “revenue” was never defined, or the table excludes refunds, or a null meant “not applicable” and got counted as zero, or the join fanned out and double-counted, or the snapshot was eleven hours stale. The model reported what it was given, correctly, in a system where nobody had written down what the data meant.
That is not a model defect. It is a missing contract, and it was missing long before the agent arrived — the agent is just the first consumer that couldn’t walk over to someone’s desk and ask.
The word is doing too many jobs
“Hallucination” gets applied to at least four different things: a fabricated fact with no source, a correct aggregation over incorrect data, a wrong aggregation over correct data, and a confident statement about state that has since changed. Four causes, four fixes, one word — which is why naming it ends the investigation at exactly the point where it should start.
The first of those — pure fabrication from nothing — is a genuine model behaviour, and it is the one everybody talks about. In production systems that touch real data, it is the rarest of the four. The other three are data problems wearing a model’s clothing.
What a data contract has to declare
If you have read Tool Contracts, this will look familiar, because it is the same idea one layer down. A tool contract tells the model what an action does. A data contract tells it what a number means. Both fail the same way: the thing that wasn’t written down becomes the thing that was guessed.
Definition
What the field actually counts, in business terms, including what it excludes. “Revenue: recognised revenue net of refunds and credits, excluding tax and intra-company transfers.” If two systems disagree on this, agent output will be brittle in a way that looks random — see Why LLMs Are Bad at Big Data for why the semantic layer is where this belongs.
Grain
What one row represents. Order, or order line? Session, or pageview? Customer, or customer-per-region? Grain is the single most common cause of numbers that are wrong by a factor of two to five, because summing a table at the wrong grain produces a plausible figure rather than an error.
Units, currency and timezone
Cents or pounds. Which currency, and converted at which date’s rate. Which timezone the day boundary falls on. Every one of these is usually implicit, and every one of them silently changes the answer.
Nullability and what null means
Null is at least three different statements: not applicable, not known, and not yet collected. They aggregate differently — one should be excluded from a denominator, one should widen a confidence interval, one should trigger a caveat. Collapse them and you get an average over a population that doesn’t exist.
Freshness and as-of
When this data was last true. Not when the pipeline last ran — when the underlying facts were captured. An agent reasoning over a snapshot has no way to know it is stale unless the snapshot says so.
Completeness
What is not in here. Which regions, channels, or date ranges are missing or partial. Backfills in progress. A dataset that silently starts in March will happily produce a “year over year” comparison.
Lineage
Where it came from and what was done to it. Covered properly in Making an Agent Explain What It Did to Your Data.
Stability
Whether the definition can change, how you would be told, and whether history gets restated. A metric that was redefined in April makes every comparison across April wrong, and nothing in the data will say so.
Ownership
Who decides what this means when there is a dispute. Unowned definitions drift, and drift in a definition is indistinguishable from drift in the business.
Four shapes this takes in practice
The undefined term. Someone asks for “active users.” Three tables could answer it, with three different definitions, and the model picks one. The number is correct for the definition it chose and wrong for the one in the asker’s head. Nobody can tell without inspecting the query — which is why the query has to be visible.
The wrong grain. The agent joins orders to order lines and sums the order total. Every order with three lines now counts three times. The result is internally consistent, has no nulls, throws no error, and is 2.6× too big. Fan-out from a one-to-many join is the most reliable way to produce a confidently wrong number I know of.
The silent null. A conversion rate computed over rows where the denominator is sometimes null, treated as zero. The rate looks fine. It is an average over a partly imaginary population, and it will look fine every time you run it.
The stale as-of. The agent reads a materialised view built at 06:00, reasons for four seconds, and reports the position as current. In a slow-moving domain this is harmless. In a domain where the number moves — inventory, exposure, spend — the agent has just made a confident claim about a world that no longer exists.
Notice that none of the four produces an error, and all four produce a number that passes a sanity check. That is the defining property of a data contract failure: it fails quietly and looks right.
Why retrieval makes this worse
Chunking a document strips it from its context. A paragraph reading “Q3 revenue was £2.9m” retrieved on its own has lost which entity, which currency, whether it was restated, and when it was written. The chunk is true and the answer built from it is false, and no amount of model quality fixes it, because the information required to be correct is no longer present.
If you retrieve over documents that contain figures, the metadata has to travel with the chunk — entity, as-of date, source, and whether the figure was later revised. Retrieval systems that store text and drop provenance are manufacturing this failure mode at scale.
What to actually do
Put the contract where the model reads it. Column descriptions, metric definitions and grain statements belong in the schema the model sees, not in a wiki page a human might consult. If the definition is not in the context, it does not exist.
Return the metadata with the answer. Every figure should arrive with its as-of timestamp, its definition, and its row count. This costs almost nothing and turns a bare number into a checkable claim.
Make violations loud rather than silent. A query that returns nulls in a denominator, or that fans out beyond an expected row count, should fail rather than round. Loud failure is recoverable; quiet wrongness compounds.
Test the contract, don’t just write it. Assert grain with a uniqueness check. Assert freshness with a max-timestamp bound. Assert completeness with a per-partition row count. Assert the definition with a reconciliation against the system of record. A contract without tests decays into documentation, and documentation is a wish.
Let the system refuse. If freshness is outside tolerance or a definition is ambiguous, the correct output is a refusal with a reason, not a best guess. The mechanics of golden questions, reconciling invariants and permission to refuse are covered in Why LLMs Are Bad at Big Data; the point here is that a refusal is only possible if the contract exists to be violated.
The reframe
If your agent produces a wrong number, resist the urge to prompt around it. Ask instead: which clause was missing? Was the term undefined, the grain unstated, the null ambiguous, the snapshot stale, the join unconstrained?
In my experience that question has an answer nearly every time, and the answer is a gap that predates the agent. Your analysts were absorbing it — carrying the definitions in their heads, knowing which table to avoid, remembering that the March data is incomplete. That knowledge was never in the system, and it was never tested, and it worked because humans quietly patched it a hundred times a day.
An agent cannot do that, and it will not tell you it cannot. It will produce a number.
The upside is that this is fixable, and the fix is worth having regardless of whether the agent ships. Every contract you write makes the data better for the humans too. That is unusual in this field, where most safety work is pure overhead — here, the safety work and the data-quality work are the same work.
Where this goes next: the access controls that keep an agent’s queries bounded are in Letting an Agent Touch the Warehouse Safely, and the broader reliability practice these sit inside is The Agent Reliability Handbook. For the taxonomy of what “hallucination” is standing in for, see Agent Failure Modes.