At 03:14 UTC, a parser returned an empty object. Downstream, the risk engine scored the asset 0.
Not zero as in "we looked and it's clean." Zero as in "there was nothing to look at." The order filled. Nobody got paged. The dashboard stayed green for another nine hours, because a green dashboard is exactly what a pipeline looks like when it has stopped having opinions.
I've spent the last three years building and auditing the plumbing behind real-time trading signals, and this is the failure mode that keeps me up: not the outage, not the exploit, not the depeg. The null. The absence of data, misread by every layer beneath it as the presence of safety.
Crypto's data stack is a chain of translations, and every translation is a place where "I don't know" can quietly become "no."
Here's the standard shape. An exchange REST endpoint rate-limits you and returns a 200 with an empty array. Your collector, written defensively, logs a warning and moves on. Your normalizer maps the empty array to {}. Your feature builder computes volume_24h = None. Your risk scorer — which is, statistically speaking, a chain of comparisons — evaluates None > threshold as false, and the asset sails through. Your execution layer sees a clean signal and fires. Elapsed time from missing data to live position: 400 milliseconds.
Nothing in that chain is unreasonable in isolation. The bug is architectural: most crypto pipelines are built with two-valued logic in a domain that requires three. Approved / Rejected. Pass / Fail. Risk / No risk. But the honest state space has always been Approved / Rejected / Unknown — and Unknown has no slot in the schema, so it inherits whatever the default is. In almost every codebase I've opened, the default leans permissive.
This isn't a crypto invention. SQL has had NULL for fifty years. IEEE 754 gave us NaN. Kleene gave us three-valued logic in the 1930s. Crypto rebuilt all of it out of JSON and optimism, and then pointed a leverage button at it.
I watched this play out during the Terra collapse in May 2022, when I lost $12,000 and spent the following week arguing with strangers on Telegram about governance. I was wrong about the framing. Governance failure was the story everyone could see. The oracle latency — specific price feed delays propagating stale values into a system with no vocabulary for "stale" — was the story that mattered. When the peg breaks, the truth arrives — but only to whoever is watching the feed, not the price.
So let me get concrete, because this is a code problem and it deserves code.
The most common version of the bug is boolean coercion across a boundary. It usually looks like this:
def score_asset(snapshot: dict) -> float:
# returns 0.0 (safe) to 1.0 (dangerous)
if not snapshot:
return 0.0 # <-- the entire article, in one line
...
if not snapshot: return 0.0 is not a defensive guard. It's a lie with good test coverage. It says: when I have no information, I will assert the most benign possible conclusion. It passes CI because the happy path is well-tested and the null path throws no exception — it produces a number, and numbers look like analysis.
The fix isn't a bigger if. It's making unknown unrepresentable as a float.
from enum import Enum
from dataclasses import dataclass
class Verdict(Enum): APPROVED = "approved" REJECTED = "rejected" INSUFFICIENT_DATA = "insufficient_data"
@dataclass(frozen=True) class Decision: verdict: Verdict score: float | None # None is legal; 0.0 is not a substitute missing: tuple[str, ...] # every field that failed to arrive ```
Two properties matter. First, INSUFFICIENT_DATA is a first-class state — it can't be silently coerced, and every downstream consumer must handle it explicitly, which means it shows up in code review. Second, the missing tuple makes absence auditable. When I retrofitted this pattern onto a signals service in Toronto, the most useful artifact wasn't the fix — it was the report of which fields had been null, and for how long. Roughly 4% of our "healthy" asset coverage had been running on stale or absent inputs for weeks. We had measured latency to the millisecond and never once measured completeness.
That's the asymmetry. Uptime is instrumented. Latency is instrumented. Completeness — the question "did the data I expected actually arrive, with the cardinality and freshness I expect?" — is almost never instrumented, because it requires declaring expectations, and declaring expectations means writing down what you don't know.

I hit the same wall auditing the MEV-Boost relay in 2023, where a race condition in block-building logic opened a window for sandwich attacks during high-volatility periods. The exploitable surface wasn't a missing check in the builder. It was the builder's model of the mempool — a model that treated a transaction it hadn't yet seen as a transaction that didn't exist. Speed reveals what stillness conceals: the bot didn't out-compute the builder, it out-observed it. The blind spot was structural, not computational.
Same pattern, one layer up. Tracing the alpha trail through the noise means, first, mapping where the noise stops arriving.
Here's the part that will annoy people building "AI-driven alpha."
The prevailing assumption this cycle — and I say this as someone who spent 30 days running an autonomous agent that paid for its own compute in USDC — is that edge comes from processing more data faster. More feeds, lower latency, better models. The bull market funds that thesis enthusiastically. Every freshly minted $100M ecosystem grant seems to buy another ingestion pipeline.
But degraded-but-plausible output is strictly worse than an outage. An outage is loud: the alert fires, the desk hedges, the position doesn't open. A null that reads as safe is silent — it produces a confident, well-formatted, wrong answer, and confident wrong answers are the only kind that get size.
The human layer has the identical bug. I've watched research pipelines return an empty field set — no title, no source, no information points, nothing but unclassified blanks — and then watched an analyst produce a two-thousand-word thesis anyway, because a blank schema is socially unacceptable in a way that a wrong one isn't. The gap gets filled with narrative. That's the architecture of belief versus the code of fact, and it is the same defect wearing a suit.
The infrastructure comparison that actually matters isn't between chains. It's between data regimes. Most rollups I've audited publish a DA commitment and call the job done; almost none publish a completeness attestation — how many batches arrived, how many were expected, what the gap distribution looked like. Which is why the DA debate is mostly theater: 99% of rollups don't generate enough data to need dedicated DA, and the ones that do aren't measuring whether it arrived. Same in custody. When I compared BlackRock's BitGo arrangement against Fidelity's in-house arm 48 hours before the ETF approval, the divergent risk wasn't in the keys — it was in the disclosure cadence. One side told you what it held. The other told you what it held and what it couldn't confirm.
Chaos is just data waiting to be organized. Absence is data too — it's just the part nobody bills for.
The next twelve months will produce a wave of incidents filed under "oracle failure," "RPC degradation," or "API rate limit," and almost none of them will be about availability. They'll be about a schema with no room for "unknown" and a default that leaned optimistic.
Watch for the first team that publishes a data-integrity SLA beside its uptime SLA — completeness defined per feed, with a public gap log. That team will look boring for a quarter.
Then something breaks, and everyone will ask how they saw it coming. The answer will be that they were measuring the thing that wasn't there.