Last week I was handed a research pipeline to review. It returned a complete, well-formed, nine-dimension analysis. Technical. Tokenomics. Market. Ecosystem. Regulatory. Team. Risk. Narrative. Supply-chain transmission. Every section populated. Every field carrying a value. Every value reading the same three characters: N/A.
Structurally, the document was flawless. The JSON schema validated. Required keys were present. No nulls where the schema forbade nulls. It contained exactly zero bits of information about the world, and it was indistinguishable, at the level of type signature, from a report that contained all of them.
I have seen this failure mode before. I have never seen it rendered so cleanly. The pipeline did not crash. It did not throw. It degraded gracefully into a beautifully typeset void and handed that void downstream as though it were a finding. The hash is not the art; it is merely the key β and a key that opens nothing is still a key.
Here is the architecture, because the architecture is the argument. Almost every AI-augmented research stack in this industry is a two-stage pipeline. Stage one extracts: it reads a source, decomposes it into information points, and writes them to a structured intermediate representation. Stage two analyzes: it consumes those points and produces the dimensions, the risk matrix, the confidence labels.
The contract between the stages is a schema. Stage one promises a list of information points. Stage two promises that every conclusion traces back to one of them.
Now remove stage one's input. Feed it nothing. What happens?
In a well-built system, stage two refuses to run. In most systems I have audited, stage two runs anyway β because the schema said "list of strings," and an empty list is a list of strings. The analyzer then does exactly what it was trained to do: it produces the shape of an analysis around the absence of one. The nine dimensions come back. The risk matrix comes back. The confidence labels come back, which is the part that should frighten you, because confidence attached to nothing is worse than no confidence at all.
Borrow the vocabulary from distributed systems and the failure becomes legible. This is a graceful-degradation bug. Someone, at some point, made a deliberate decision that a partial response beats no response β that a fallback value beats an exception. That decision is correct in a CDN serving a cached image and catastrophic in a system whose output is consumed as fact. Stale-while-revalidate is a good pattern for HTML. It is a liquidation event for a price feed. The pipeline I reviewed had inherited the pattern without inheriting the reasoning, which is how most bad architecture spreads: as a default nobody remembers choosing.
This is not a new problem. It is the oracle problem wearing a new hat.
Null is not a value; it is the absence of one, and most pipelines cannot tell the difference.
Solidity gets this right in a way Python does not. When a contract calls require(price > 0), the call reverts. The entire transaction unwinds. State is not partially updated. The caller receives an error, and β critically β the caller has no choice but to handle it, because there is no return value to ignore. Fail-closed is not a design preference in the EVM. It is the default physics.
Python has no such physics. None propagates. A function returns None, the next function receives None, and unless someone wrote if x is None: raise at every boundary, the None flows downstream until it hits something that formats it. Formatting None is always possible. That is the whole problem. f"{None}" produces the string "None", which is a perfectly valid string, which is a perfectly valid value in your schema.
I spent three weeks in 2021 mapping IPFS pinning across major profile-picture projects, and I found the same structural error in a different medium: over 60% of "permanent" NFTs resolved through centralized gateways already failing under load. The metadata schema said the asset was decentralized. The schema was not lying. It was describing a type, and the type was satisfied by a pointer that resolved to nothing. A pointer is not the thing it points to; a schema is not the thing it describes. Everyone nodded at that sentence. Nobody changed their integration.
Schema conformance is not truth, and the industry keeps rediscovering this the expensive way.
Consider how Chainlink's latestRoundData actually behaves. It returns five values, and two of them matter more than the price: updatedAt and answeredInRound. The price is the headline. The timestamp is the truth. A price with a stale updatedAt is not a price β it is a memory of a price, and it is a memory a liquidation engine will happily act on if nobody wrote the check.
Most forks omit the check. I have read a lot of these forks. The pattern is consistent: the integration copies the call, copies the destructuring, and drops the require(updatedAt >= block.timestamp - HEARTBEAT). It works in testing. It works in production for months. It fails on the one day the feed stops updating and the market moves, and it fails by liquidating positions against a price that has not existed since Tuesday.
An oracle does not tell you the price. It tells you the last time someone told it the price. Those are different statements, and only one of them is safe to build a liquidation engine on.
The schema itself does quiet work here, and the work is invisible. A field typed as string accepts "N/A". A field typed as string | null accepts null. Neither type says anything about whether the underlying quantity was measured. To make ignorance legible you need a second field β a reason code, an enumeration of UPSTREAM_TIMEOUT, SOURCE_EMPTY, PARSE_FAILURE, NOT_APPLICABLE β and almost nobody writes it, because adding a field to a schema is work and the first version always ships without it. A schema that permits absence without requiring a reason is a schema that launders ignorance into output. The N/A is not the problem. The unqualified N/A is.
The same epistemic error sits underneath the interest rate models everyone treats as physics.
Aave and Compound price debt through a kinked curve: utilization below a threshold yields one slope, above it yields a steeper one. The parameters β base rate, slope one, slope two, the kink point β were chosen. Not derived. Chosen, in a governance forum, by people who had a target in mind. There is no arbitrage that forces the kink to sit where it sits. There is no external market that disciplines the slope. The curve is a policy dressed as a mechanism, and it gets audited as if it were physics, which means the parameter review focuses on the slope values and never on whether the kink should exist at all.
And the variable it consumes β utilization, total borrows over total liquidity β is a derived signal, not a measurement of demand. I have sat in those reviews. Nobody asks whether utilization is the right independent variable. It is the available one, and available variables win.
In 2020 I built a Python simulator to model liquidity provision on the Uniswap v2 constant product formula, and I found the standard impermanent-loss derivation circulating in blog posts was wrong. Not approximately wrong. Structurally wrong, because it assumed a geometric mean the AMM does not compute. I published a ten-page correction. It got attention from quant researchers and almost none from the people actually deploying capital.
That is the pattern. The derived signal is treated as ground truth because it is easy to read, and the correction is ignored because it is hard to read. A pipeline that emits N/A for every dimension is making the same class of statement as an oracle with a stale timestamp: it is telling you the ground truth is unavailable. The question is whether anyone downstream is checking.
Fluency is the gradient, and hallucination is the path of least resistance down it.
Language models are trained to produce plausible continuations. In a research context, "plausible" is measured by structure, register, and internal consistency β not by correspondence to an external state of affairs. When the input is empty, the model is not confused. It is being asked a question whose most probable answer, given the training distribution, is a well-formed analysis. The empty-input condition does not register as an error, because nothing in the loss function penalizes it.
In 2017, at twenty-five, I spent twelve hours a day auditing the Solidity source for the Golem Network's token distribution contract. I found three integer overflow vulnerabilities in the pledge logic and submitted a pull request with a mathematical proof of the exploit. The founders rejected it as too academic. The vulnerabilities were real. The proof was correct. Correctness was not the currency the market was trading in.
I have carried that lesson into every system I have reviewed since. The most dangerous output of a model is the one that looks like an output. An error message announces itself. A blank page announces itself. A nine-dimension analysis with confidence intervals, rendered in clean JSON, announces nothing β and it will be consumed, cited, and acted upon by a reader who has no way to see the void underneath.
Downstream contamination is where the cost lands, and it lands months later.
I wrote a whitepaper-style analysis of MakerDAO's liquidation engine after the 2022 drawdown, tracing the specific code branches that let cascading liquidations propagate through the debt ceiling. What struck me was not the failure itself but the latency: the mechanism had been readable in the source for years. The information was public. The comprehension arrived late.
A fabricated analysis behaves the same way, but worse, because it enters the corpus. It gets posted to a governance forum. It gets summarized into a treasury proposal. It gets scraped into the next training set, where it becomes evidence that the pattern it describes is real. Contamination compounds. By the time someone traces a bad parameter choice back to its source, the source has been cited four hundred times and the original input β the empty one β is unrecoverable.
In 2026 I designed an interface specification to let autonomous agents sign transactions through zero-knowledge proofs, specifically to prevent model hallucination from producing irreversible financial errors. The design principle was narrow and deliberate: the agent proves a statement about its inputs. It does not assert a conclusion. If the inputs are empty, the proof is unsatisfiable, and the transaction does not exist. Forty percent fewer failed transactions across the integration set. A revert is a truth claim. A null is a lie waiting for a type annotation.
Here is the part that will cost me some readers. The all-N/A report I opened with is the most trustworthy artifact in crypto research I have seen this month. It has a boundary. It states, in a format anyone can parse, exactly what it does not know β and that statement is falsifiable, auditable, and correct.
Nothing else on my desk this week has that property. The decks do not have it. The threads do not have it. The nine-dimension analyses with twelve confidence intervals do not have it, which is precisely the problem: confidence is cheap to generate and expensive to falsify, so the market generates it in unlimited quantity and falsifies almost none of it.
The contrarian claim is not that we should build better extractors. It is that the valuable primitive is a revert. Fail-closed research. And the reason nobody builds it is that fail-closed research does not get paid: a report that says "insufficient information" earns no engagement, wins no retainer, gets no repost. The incentive gradient selects for fail-open at every layer, from the pipeline to the publication.
Which brings me to the attacker's cheapest vector. Everyone is worrying about prompt injection β getting a model to say something it should not. That is the expensive path. The cheap path is starvation. Feed the pipeline nothing, let it fill the void with fluency, and let the output do the work. You do not need to compromise the model. You only need to wait for it to be handed an empty list.
As agents acquire signing authority, the empty-input problem stops being an epistemic nuisance and becomes an exploit primitive. My forecast: the first material loss attributed to an autonomous research or execution agent will not come from a jailbreak. It will come from a pipeline that ran successfully on nothing, produced a confident artifact, and was believed by a system that had no staleness check. The vulnerability is not in the model. It is in the require statement nobody wrote.