Last week a due-diligence report landed in my inbox. Nine sections. Technical architecture. Token economics. Market positioning. Ecosystem slot. Regulatory exposure. Team and governance. Risk matrix. Narrative durability. Supply-chain transmission.
Every cell in every table read "N/A โ insufficient information."
The document ran 2,400 words. It had been generated by an automated research pipeline, reviewed by nobody, and forwarded to an investment committee of three people who were sizing a position that same afternoon.
Nobody noticed the input was empty.
That is the story. Not the missing data. The fact that a system produced a structured, confident-looking, nine-dimension artifact out of nothing, and the humans downstream read structure as substance. In a bull market where every desk is under pressure to cover more names than it can actually read, this is not an edge case. It is the default operating mode of the entire research layer.
The two-stage contract
The pipeline had two stages. Stage one extracts "information points" from a source article โ title, claims, named protocols, timestamps, source-quality grade. Stage two takes that array and runs it through nine analytical dimensions, each of which must cite its evidence by index: โ basis: [information point #4].
Stage one returned an empty array. Not an error. Not a null. An empty list.
Stage two did what it was told. It iterated over zero elements, produced zero citations, and filled every dimension with the string "N/A โ insufficient information." Then the renderer formatted it. Headers. Tables. Checkboxes. A risk matrix with six rows and six columns of N/A. A composite verdict of "cannot form a judgment." A closing section titled "Executive Summary" with a red status marker and a recommendation to re-run stage one.
To its credit, the pipeline refused to speculate. It wrote, in plain language, that this was not an analysis but a diagnosis of missing input. It enumerated what each dimension would require in order to activate. It was, by any honest reading, the correct output.
And it was still a catastrophic failure, for reasons that have nothing to do with honesty.
The template is the product
Follow the incentive. Research vendors in this cycle are not selling accuracy. They are selling coverage โ nine dimensions, twelve dashboards, forty-two signals, one PDF. Coverage is legible in a screenshot. Accuracy is not. When your sales motion depends on a screenshot, your architecture drifts toward always producing a screenshot.

An empty array is a legal input to that architecture. Nothing in the schema says otherwise. information_points: [] validates. The JSON is well-formed. The types are correct. Every layer behaves exactly as specified, and the composition is fatal.
I have watched this shape appear in crypto tooling three times now: once in a portfolio tracker that rendered a 0% drawdown for a wallet whose price feed had silently disconnected, once in a liquidation bot that read an empty order book as infinite liquidity, and now in the research layer. Same disease. Different organ.
The nine-dimension template is not an analytical framework. It is a coverage claim dressed as a methodology. The moment a vendor ships it, the incentive is to populate all nine boxes โ and when the source material only supports two, the remaining seven get filled with inference, hedge language, or the word "medium." The empty report in my inbox was the rare case where the vendor's system refused. That refusal is now a competitive disadvantage in a market that rewards throughput.
Zero-value semantics
The underlying bug is older than any of this.
In the Solidity 0.4.x era, an uninitialized storage pointer did not throw. It returned zero-values, silently, and if you wrote to it you overwrote slot zero โ the state variable that held your contract's ownership. The compiler was correct. The EVM was correct. The developer's mental model was wrong: they assumed a variable with no assignment would fail loudly. It didn't. It defaulted to zero and kept going.
address(0) is the same disease in a different organ. A token transfer to the zero address "succeeds" on contracts that never added the guard. The balance is debited. The tokens are gone. No revert, no event anyone reads, no error surface.
I spent six months in 2017 reverse-engineering the vesting contracts of a top-ten ICO. The bug I found was an integer overflow in the distribution math โ a boundary condition in a function that everyone had reviewed, in a codebase that had been audited twice. Twelve million dollars of allocation was reachable through an input nobody had fuzzed. I reported it privately through an encrypted channel and got no public credit, which was fine. What I took from it was narrower and more useful: auditors check what the code does. Almost nobody checks what the code does when it is handed nothing.
The fix in every one of those cases was a guard, not a smarter compiler. require(to != address(0)). require(points.length > 0). A system that cannot distinguish "no data" from "data that says nothing" will eventually treat them identically, and the cost of that confusion lands on whoever acted on the output.
The report in my inbox had no guard. It had a renderer.
Where the friction actually lives
Everyone in this cycle wants to talk about inference cost. GPU hours. Token burn. Throughput per dollar.
The gas isn't the issue. It's the friction of poor architecture โ the cost of a system doing work it should have refused to do, in a format that makes the refusal invisible.
The pipeline spent compute on nine dimensions that could not be populated. It spent latency on a render pass. It spent human attention โ three people, an afternoon, a committee slot โ on a document whose entire information content was one bit: no source material. That bit was available at millisecond zero, before the first inference call. It was buried under 2,400 words of scaffolding.
I forked a yield aggregator in 2020 for exactly this reason. Gas was at 300 gwei and the contracts were "correct." They also read the same storage slot four times per loop iteration, because the developer optimized for readability in a codebase where readability had a measurable price. We packed the state variables, cached the reads, cut gas 22%, saved roughly $50,000 across a month of testing. The lesson was not "optimize everything." The lesson was that structure imposes cost whether or not anyone measures it. A nine-section template charges the reader every single time, even when eight sections are empty.
The propagation chain
Trace the empty array through the stack, because each hop is individually defensible and jointly lethal.
Hop one: extraction. The parser fails on the source โ malformed HTML, paywall, wrong encoding, a redirect to a consent page. It returns []. Correct behavior for a parser that cannot parse.
Hop two: validation. The schema declares information_points as an optional array defaulting to []. The document validates. Correct behavior for a permissive schema.
Hop three: analysis. The dimension functions map over the array. Zero iterations, zero output, zero exceptions. Correct behavior for a pure function.
Hop four: rendering. The template has nine sections and renders nine sections. Correct behavior for a template.
Hop five: delivery. The report is sent. Correct behavior for a mailer.
Five correct components. One wrong system. This is the composition problem, and it is the same problem I stress-tested in 2022 on a Layer 1 that claimed to solve the trilemma. Every individual component โ consensus, networking, finality gadget โ passed its own suite. Under a 15% validator dropout, finality lagged forty minutes and assets would have been frozen. The bug was not in any component. It was in the contract between them, and nobody had written a test for the contract between them.
Three places this bites on-chain
First: oracle feeds. An aggregator that receives zero responses from its reporters and returns the median of an empty set. Depending on implementation that is either a revert or a zero. In two production feeds I have reviewed, it was a zero, and a lending market priced collateral against it.
Second: agent tool-calling. This is the 2026 surface. An LLM agent queries an indexer, gets an empty response because the subgraph is lagging, and โ because the tool schema marks the field optional โ proceeds to reason about a position it believes is flat. The agent is not hallucinating. It is reasoning correctly from a silently zeroed input. That is worse, because hallucination is at least detectable by a second model. Silent zeros pass every consistency check you own.
Third: the research layer itself. Which is where we started.
What fail-closed looks like
I rebuilt the stage-one/stage-two contract on a fork last month, as an exercise. Four changes.
Require provenance at the type level. Every information point carries a source span โ byte offsets into the original document. If the span does not resolve, the point does not exist. No span, no point, no citation, no dimension.
Make emptiness a first-class error. points.length == 0 throws InsufficientSourceError and halts before analysis. The renderer never sees an empty array, because the renderer sits downstream of a gate that will not open.
Kill the default. Optional fields with [] defaults are how systems learn to lie politely. If a field is required for a dimension to run, it is required at the schema level, and its absence is a hard failure with a named exception.
Count the claims. Before rendering, assert sourced_claims >= 3. Under three, the output is a stub, and it is labeled a stub in the subject line. Not in a footnote.
None of this is novel. It is the discipline of require() in a token contract, and it exists for the same reason: the expensive failure is never the one that reverts.
The spec-completeness problem
There is a second-order version of this that the NFT market taught me. In 2021 I audited royalty enforcement across fifteen marketplace backends and found five edge cases where ERC-721 and ERC-1155 implementations disagreed about what "royalty" meant โ marketplace-level versus token-level, on-chain versus off-chain settlement, batch transfers, zero-address recipients, and self-sales. Every implementation was individually reasonable. Every one passed its own tests. The failures all lived in the gaps between specs that everyone assumed were complete.
A nine-dimension research template is a spec. And like every spec written by the party that also sells the implementation, it is written to be satisfiable. That is the blind spot: nobody writes a spec that defines what happens when the input is nothing, because defining that case admits the case exists.
How to test for it
Run null campaigns. Not unit tests โ campaigns. Feed the pipeline an empty string, a paywalled URL, a 404, a valid page with zero extractable claims, a page in the wrong language, and a page that is pure advertisement. For each, assert one of two outcomes: a named exception, or a labeled stub. Anything else is a finding.
Then run the poisoned variant. Take a source document you control, embed an instruction that flatters the extraction stage into producing a plausible non-empty set, and see whether stage two cites it. If it does, you do not have a research tool. You have an attack surface with a subscription fee.
The honest artifact
Here is the uncomfortable part.
The empty report was the most honest artifact in the entire stack.
It said, in every cell, that it did not know. It refused to speculate. It told the reader exactly what input would activate each dimension. Compare that against a filled-in report built on a single scraped paragraph and four inferences โ the kind that ships every day in this market, with a bullish composite score and a risk matrix full of "medium" โ and the empty one starts to look like the responsible product.
Which means the failure was not epistemic. It was presentational. The system told the truth in a format that read as a lie.
And that points at the vulnerability nobody is pricing. Empty input is detectable. A pipeline that returns nothing is annoying but safe. The dangerous state is a pipeline whose stage one returns a plausible, non-empty, poisoned set of information points โ because then stage two runs to completion, cites its sources by index, and produces nine dimensions of confident analysis built on a document an attacker wrote.
I ran that exact attack in 2026 against an LLM agent framework wired to a zk-rollup. The oracle feed was a text channel. A payload that read like ordinary prose โ with an embedded instruction โ caused the agent to reinterpret transaction output and route value to an address of the attacker's choosing. Two million in simulation. The patch was not a better model. The patch was a typed oracle boundary and a signature check on the feed payload.
Prompt injection is not a model problem. It is a data-boundary problem, and it is the same class of bug as an unvalidated external call. The industry keeps misclassifying it because the exploit surface looks like language instead of bytes, and we have a century of discipline around bytes.
Vulnerabilities aren't in the model. They're in the contract between the model and everything it trusts.
Takeaway
Expect the first nine-figure loss from an AI research agent inside eighteen months. Not from a hallucination โ hallucinations revert, eventually, when someone checks. From a silently empty or silently poisoned input that every component in the stack handled correctly.
The audit surface is not the model. It is the schema between stage one and stage two. Ask any vendor selling agentic due diligence one question: what does your pipeline do when the source document fails to load? If the answer contains the word "gracefully," walk.
Optimization isn't a throughput number. It's about respecting the user enough to tell them when you have nothing. Code that doesn't revert on empty input isn't ready for mainnet reality โ and neither is a research stack that renders a risk matrix out of an empty array.