Menu
Dev.to #architecture·September 17, 2026

Handling Ambiguous Empty Results in Distributed Systems

This article highlights a critical, often overlooked, system design problem: ambiguous empty results. When a service or agent returns an empty value (like an empty array or null), it can signify both successful operation (e.g., "no findings") and various failure states (e.g., "check never ran," "tool unavailable"). This ambiguity can lead to incorrect inferences, data corruption, and silent failures, emphasizing the need for explicit communication of execution context within responses.

Read original on Dev.to #architecture

The Problem of Ambiguous Empty Results

In distributed systems, an empty result, such as an empty array (`[]`) or `null`, often serves multiple purposes. It can correctly indicate a lack of findings (e.g., "no diagnostics found"), but it can also mask underlying issues like a failed execution, an unavailable service, or a process that never started. This ambiguity leads to systems inferring success when a failure occurred, which can propagate incorrect state throughout the architecture. The core issue is that "the difference was never transmitted."

⚠️

Silent Failures and Bad Inferences

An ambiguous empty value can be ingested into crucial system components like deduplication keys, cache entries, idempotency guards, or append-only logs. If a system interprets an empty failure state as a clean success, this incorrect inference becomes durable. Later reads of this state will falsely certify code or processes, preventing re-investigation and masking real problems.

Limitations of Sibling Calls and Weak Liveness Checks

A common but flawed approach to resolve ambiguity is to introduce sibling API calls (e.g., a `server_status()` endpoint). This strategy fails for two main reasons: it relies on the caller remembering to check the status, undermining the goal of a self-contained, bounded tool surface; and it introduces race conditions, as a status observed at one moment does not guarantee the state during the subsequent request execution. Simple liveness or availability bits are also insufficient, as a process can be "alive" but still useless or operating on outdated data.

Robust Solutions for Explicit State Communication

The solution lies in enriching the response itself with sufficient execution context. A return type should explicitly distinguish between different states, such as `observed`, `unavailable`, and `not_run`. This ensures that an empty `findings` array only implies a negative conclusion when the `state` is `observed` and the `scope` of the observation is clear. Rejecting schemas that allow `state: "observed"` without proper coverage information prevents the re-introduction of ambiguity.

json
{ "state": "observed", "scope": { "revision": "<content digest>", "target": "<path>" }, "findings": [] }
  • `observed`: The check completed successfully over the declared scope. Only then can an empty `findings` array be interpreted as a negative conclusion.
  • `unavailable`: The tool could not complete due to a specific, typed reason (e.g., `harness_dependency_missing`). This reason should have a finite reuse lifetime.
  • `not_run`: No attempt was made, often due to upstream budget or scheduling decisions (e.g., `scheduler_budget_exhausted`). This should inform scheduling rather than produce a verdict.

Furthermore, deduplication keys and cache entries should incorporate the reason class, code revision, and environment fingerprint for operational failures. This ensures that even when skipping re-execution, the decision is based on a precise, justified, and time-bounded record, rather than a permanent, ambiguous abstention. While these solutions add complexity in terms of more fields, validation paths, and storage, they provide inspectable justifications for every skipped check and verifiable execution context for every empty result, significantly improving system reliability and observability.

Error HandlingAPI DesignDistributed SystemsObservabilityData IntegrityState ManagementReliabilitySystem Design Patterns

Comments

Loading comments...

Architecture Design

Design this yourself
Design an API for a distributed diagnostic or analytics system where agents report findings. Focus on how to structure API responses to explicitly differentiate between an empty result meaning 'no findings' (successful execution with no issues), 'tool unavailable' (execution failed due to an external reason), and 'check not run' (skipped due to system policy or resource constraints). Detail the response schemas, how to handle these states in clients, and the impact on caching, deduplication, and observability.
Practice Interview
Focus: robust API response design for explicit state communication