How Agents Should Handle Missing Data

How Agents Should Handle Missing Data

E
By Etzal Earth
13 min read

An agent queries a spatial API for the buildings inside a bounding box and receives an empty array. The status is 200, the response shape is valid, the list has zero elements. Every observable property of that exchange says success. The agent now has to decide what the emptiness means, and there are at least three incompatible answers.

The first is that the provider has no coverage in that area. The second is that the provider has good coverage and the area genuinely contains no buildings. The third is that something failed quietly: a tile did not load, a partition was skipped, an upstream request timed out and a wrapper converted the error into an empty list because that was the least disruptive thing to return. All three of those cases serialize to the same handful of bytes.

Collapsing them is the root cause of most confidently wrong spatial analysis. The failure rarely announces itself. The pipeline runs, the numbers come out, the report reads cleanly, and somewhere inside it a district that was never surveyed has been described as a district with no schools.

The three meanings of empty

It helps to name the three cases precisely, because the correct response differs for each.

  • No coverage. The provider does not hold data for this area at all. The query was well formed, the service was healthy, and the honest answer is that the question cannot be answered from this source. Nothing about the world has been observed.
  • Observed absence. The provider holds data for this area, the data is current enough to trust, and the feature class requested does not occur there. This is a real finding about the world.
  • Retrieval failure. The provider holds data, the world contains features, and the request did not successfully retrieve them. This is a finding about the system, not about the world.

Only the second case is evidence. The first is a gap in the map. The third is a bug. An analysis that treats all three as zero will happily report that a rural subdistrict has no clinics when what actually happened was that the building layer for that province has never been imported.

The asymmetry matters. Observed absence is genuinely useful: it tells a planner that a service does not exist where it should, and that is often the whole point of the analysis. No coverage tells the planner only that they need a different source. Treating the second as the first wastes an insight. Treating the first as the second manufactures one.

Why a model reads emptiness as absence

Language models are strongly biased toward interpreting an empty result as a negative finding, and the bias has structural causes rather than being a quirk of any one model.

An empty list is syntactically complete. Nothing about it looks like an error, so nothing triggers the recovery behaviour a model would apply to a 500 or a malformed payload. The model has a task to finish, and an empty list is the only piece of evidence in front of it that bears on the question. The path of least resistance is to use it.

Training data reinforces this. In most software, an empty collection means the collection is empty. A search over a complete index that returns nothing means the index contains nothing matching. Geospatial data violates that assumption constantly, because the index is never complete and its incompleteness is not uniform. The model brings the general prior and the domain quietly breaks it.

Then there is the pull of the task itself. An agent asked to score five candidate sites will produce five scores. If one site returns nothing for the amenity layer, the agent has a strong incentive to interpret that as an amenity desert rather than to say the comparison cannot be completed. Refusing to answer feels like failure to a system whose objective is to answer. The design job is to remove the ambiguity before the model has to resolve it, because when the model resolves it, it resolves it toward the answer.

Designing a response that removes the ambiguity

The fix belongs in the API, not in the prompt. A response that carries the distinction explicitly makes the correct interpretation the cheapest one to reach.

Three properties do most of the work. First, coverage status as a first-class field, present on every response regardless of result count, with values that map onto the three cases: covered, not covered, partial. Second, an explicit count of what was searched rather than only what was found, so an empty result sits next to a statement about the search space. Third, a per-layer breakdown when a response spans multiple sources, because a query touching buildings, roads and land use may have three different coverage statuses inside one answer.

A response saying that zero buildings were found within a covered extent last updated in a stated period is a different statement from one saying zero buildings were returned because the building layer for this region is not loaded. The first supports a conclusion. The second forbids one. If those two responses are structurally identical apart from a note buried in documentation, the documentation will lose.

The same applies to failure. A wrapper that catches an upstream timeout and returns an empty list is not being resilient, it is laundering an error into a finding. If a component of a composite query failed, the response should say which one, and the overall result should be marked partial. Silence about a failed subquery is the single most damaging convenience in this whole area, because it is invisible in logs, invisible in tests that only assert on shape, and invisible in the final report.

Field naming carries more weight than it seems to. A field called results with an empty array invites the reading that there are no results. A field called features accompanied by coverage: none and searched_area_km2: 0 is much harder to misread.

Unknown has to survive the arithmetic

Even with a correct response, the distinction usually dies in the next step, when the values enter a calculation. Most arithmetic has no representation for unknown, so somebody substitutes zero, and zero is a specific, confident, wrong claim about the world.

The substitution is rarely deliberate. It happens because a weighted sum needs a number in every slot, because a division needs a denominator, because a chart needs a value to plot. Each of those is a small local decision that quietly converts a gap into a measurement.

Propagating unknown means making a few choices explicit rather than incidental. A missing input to a sum should not contribute zero, it should reduce the number of contributing terms and be recorded as such. A ratio with an unknown denominator should produce unknown, not infinity and not a clamped value. A score built from weighted factors where one factor is unavailable should either redistribute that factor's weight across the remaining factors, which changes the meaning of the score, or refuse to produce the score, which is often the more honest option. Both are defensible. Silently scoring the missing factor as zero is not, because it penalizes the subject for being unobserved.

Redistribution needs its own caveat. If four of five factors are present and the fifth is unknown, redistributing the missing weight produces a score on the same nominal scale as a fully observed score, and the two are then compared as if they were equivalent. They are not. The redistributed score carries more variance and rests on a narrower base. Carrying a completeness figure alongside the score, meaning the fraction of the intended weight that was actually satisfied, keeps the difference visible. A score of 72 at full completeness and a score of 72 at partial completeness should not appear in the same column without a marker.

There is a cruder version of the same rule that is worth stating plainly: never cache a partial result as if it were complete. A partial computation written into a cache loses its provenance the moment it is read back, and every downstream consumer inherits a number with no way to know it was assembled from a subset.

When imputation is legitimate

Imputation is not forbidden. It is a normal statistical technique with a clear set of conditions, and the failure mode is applying it where those conditions do not hold.

Imputation is reasonable when the quantity is genuinely continuous across space and the surrounding observations constrain it. Elevation at an unsampled point between two known points is a fair candidate: terrain is continuous, interpolation error is bounded by the local gradient, and the estimate can be given an uncertainty. Temperature between two stations, population density interpolated within a homogeneous block, a road surface attribute inferred from the rest of the same way: all of these have a physical or administrative reason to expect continuity.

Imputation is not reasonable when the quantity is discrete, rare, or the whole point of the question. The presence of a hospital is not interpolable. Two neighbouring districts each having a hospital tells you very little about the district in between, because facilities are sited by decisions, not by diffusion. The same applies to anything the analysis is meant to detect: if the deliverable is a list of underserved areas, imputing service levels into unobserved areas destroys the deliverable.

A second condition is often skipped. Imputation assumes the missingness is not related to the value being imputed. In geospatial data that assumption fails routinely, because coverage correlates with wealth, density, and mapping-community activity. Areas with missing data are systematically different from areas with present data. Imputing from the observed population into the unobserved one therefore imports a bias in a predictable direction, usually making poorer and more rural areas look more like richer and denser ones than they are.

The practical rule: impute only when the mechanism producing the value is continuous, only when the missingness is plausibly unrelated to the value, and only when the imputed value is tagged as imputed all the way through to presentation. An imputed value that loses its tag is indistinguishable from a measurement, and it will be quoted as one.

Which emptiness is worth retrying

An agent that receives an empty result has to decide whether to spend another call. The three cases give three different answers.

No coverage is not worth retrying against the same source, ever. The correct move is a different source, a different resolution, or a report that the question is unanswerable from what is available. An agent that retries a coverage gap three times with exponential backoff has burned latency and tokens to receive the same honest answer three times.

Retrieval failure is worth retrying, and the response should say so. A transient upstream timeout, a rate limit, a partition that was mid-refresh: all of these justify a second attempt, ideally with a hint about how long to wait. This is the case where an error response is far more useful than an empty one, because an error can carry a retryable flag and the empty list cannot.

Observed absence is worth retrying only in the sense of widening the question. If no clinics were found inside a 2 kilometre buffer of a covered area, the useful next call is a larger buffer or a broader feature class, not the same call again. An agent that understands it received a real negative can escalate the search intelligently. An agent that suspects the negative might be a gap cannot do anything intelligent at all, which is why the ambiguity costs more than the missing data itself.

Reporting what could not be determined

The final output has to carry the gaps, and this is where most implementations give up, because a clean number looks more professional than a number with a caveat attached.

The pattern that holds up is to state what could not be determined alongside what could, in the same register and with the same prominence. Not a footnote, not a disclaimer block at the end that no reader reaches, but a parallel statement. A flood exposure figure reported as an estimate covering the portion of the affected area where building data exists, with the remaining portion named and quantified as unassessed, is usable. The same figure reported as a single number is not, because a reader has no way to know which of those two things they are holding.

Three habits make this workable in practice. Report the denominator, meaning how much of the intended analysis area or factor set was actually covered. Name the specific layer or source that was missing rather than saying data was incomplete, because a named gap is actionable and a vague one is decorative. State the likely direction of the error where it is known, since coverage gaps in spatial data are rarely symmetric and a reader who knows an estimate is a floor rather than a midpoint will use it correctly.

For agent-facing output the same information should be machine readable, not only prose. A confidence value per figure, a list of unresolved inputs, and a completeness fraction let a downstream agent make its own decision about whether the number is fit for its purpose.

Testing for the failure

This class of bug does not surface in normal testing, because normal test fixtures come from areas with good coverage.

A test suite that catches this needs fixtures for all three cases deliberately: a query in a region with no loaded coverage, a query in a well covered region that genuinely returns nothing, and a simulated upstream failure. The assertions should be on the coverage metadata and on the downstream behaviour, not only on the response shape. A test asserting that the response is a valid empty list passes in all three cases and proves nothing.

The higher value test is at the analysis layer. Take a complete input, remove one layer, and check that the final score changes in the way the design says it should. If the score stays the same, unknown is being silently zeroed somewhere. If the score drops without the completeness figure dropping, a gap is being scored as a bad value. Both are common and both are hard to find by reading code, because the substitution usually happens in a helper that looks entirely reasonable in isolation.

The failure that ships

The version of this that reaches production is almost never a crash. It is a number that is slightly wrong in a consistent direction, produced by a pipeline that ran without incident, presented with the same formatting as the numbers that are right.

An agent that has to guess what an empty response means will guess absence, because absence is the interpretation that lets it finish. Every design decision in an API or an analysis pipeline either removes that guess or leaves it in place. Leaving it in place is a decision too, and the cost lands on whoever reads the output furthest downstream, who has the least context and the most confidence in the number.