The Empty Preimage: When a Pipeline Returns Success and Writes Nothing

CryptoRay
Markets

Last week I ran a routine input against a two-stage analysis pipeline and watched it return a perfectly formed nine-section report. Every heading was present. Every table had borders. Every field β€” technical assessment, token economics, regulatory exposure, governance health β€” carried the same four words: "N/A β€” insufficient information." No exception thrown. No revert. Exit code zero. The system had executed flawlessly and produced nothing.

If you have ever audited a Solidity contract, you already know why that output made my skin crawl.

The pipeline is architecturally simple, which is precisely what makes it dangerous. Stage one is a decomposition function: it consumes raw text and emits an "information point list" β€” atomic, independently verifiable facts. Stage two is a pure function of stage one. It maps those facts across nine analytical dimensions. The dependency graph is a straight line. Stage two never sees the original document; it only sees stage one's output. That is good design. It is also the design that failed.

When stage one returned an empty list, stage two should have reverted. Instead it treated "empty" as a valid input, iterated over nothing, and emitted a template whose structure survived while its payload evaporated. The schema held. The meaning did not.

In EVM terms, this is the CALL-to-empty-address footgun, and it is older than most of the people trading today. Since the earliest revisions of the Yellow Paper, a CALL to an account with no code does not fail. It returns 1 β€” success β€” with empty return data. Gas is consumed, the stack is clean, and a caller that checks only the boolean believes the call worked. I spent six months in 2017 auditing gas-cost edge cases for CALL operations against the Yellow Paper, and I identified three variants that could drive unoptimized contracts into unbounded loops. The lesson has not changed in a decade: success is not the same as effect.

Here is the buggy pattern, reduced to pseudo-code:

(bool ok, bytes memory data) = target.call(payload);
require(ok, "call failed");
process(data);          // data is empty; ok was true

And here is the patch:

require(target.code.length > 0, "no code at target");
(bool ok, bytes memory data) = target.call(payload);
require(ok && data.length > 0, "empty response");

Map the two checks onto the pipeline. The boolean ok answers one question: "did the model return a well-formed response?" The length check answers a different, harder question: "does the response contain at least one fact?" The pipeline checked only ok. It verified syntax and forgot semantics.

The invariant it violated is easy to state and hard to enforce without discipline. For any analysis function A that maps a document D to a report R, A is only defined when the extracted fact base F(D) is non-empty. Formally: dom(A) = { D : |F(D)| β‰₯ 1 }. An empty fact base is not a document the function may consume. It is outside the domain. The pipeline breached its own domain constraint because the constraint existed in the specification and never existed in the code.

There is a second, quieter cost. The pipeline still burned compute to produce its nothing. Tokens were spent, latency was incurred, and the output was a document with an information content of exactly zero. That is the analysis-layer equivalent of paying gas for a no-op β€” a state transition that touches nothing and charges you anyway. Machines are very good at this. They do not get embarrassed.

This is what I called the semantic consistency problem in a 2026 whitepaper on autonomous DeFi. Machine-readability cuts both ways. A schema-conformant document is trivially parseable β€” and trivially misleading. The moment you optimize for structure, you create a surface that empty content can occupy without friction. A malformed output announces its failure. A well-formed empty output conceals it.

The Empty Preimage: When a Pipeline Returns Success and Writes Nothing

Now run the adversarial path. Suppose an autonomous agent consumes that report. The "N/A" fields are not neutral placeholders; they are implicit instructions to skip. An agent reading "regulatory exposure: N/A" may proceed as though the asset were compliant. An agent reading "audit status: N/A" may treat the absence of a flag as the absence of a risk. The empty preimage does not stay contained in the analysis layer. It propagates downstream, and every downstream consumer inherits the assumption that the upstream function succeeded.

The deeper problem is cultural, not technical. We have spent a decade training ourselves to celebrate pipelines that never crash. Uptime. Fault tolerance. Graceful degradation. This is the vocabulary of reliability engineering, and it is the wrong vocabulary for verification engineering. A pipeline that cannot revert is not robust. It is a silent-failure machine. The most dangerous artifact is not an error page β€” nobody trusts an error page. The most dangerous artifact is a complete, confident, well-formatted document that says nothing at all.

A bug is just an unspoken assumption made visible. Here the assumption was "empty input is acceptable input." It had been unspoken for as long as the pipeline had run, because no one had ever fed it a document with zero extractable facts. The assumption was never load-bearing until it was.

I want to be fair to the analyst whose output triggered this. The document in question refused to fabricate. Faced with an empty fact base, it declined to invent projects, data, or conclusions, and it said so explicitly. That refusal is correct, and it is rarer than it should be. Hallucination is the failure mode everyone fears: stage one invents a fact, stage two launders it into a conclusion, and the report reads beautifully. What happened here was the inverse. The system refused to hallucinate β€” and then dressed its refusal in the costume of a finished product. The right move was not a filled-out template. It was a revert.

Security is not a feature; it is the architecture. A domain constraint that lives only in a specification is not a constraint. It is a hope.

Which brings me to the forecast. As agents begin to transact autonomously, this class of bug leaves the notebook and acquires a wallet. The next wave of exploits will not be reentrancy. It will not be oracle manipulation. Both of those are known surfaces with known patches, and the auditors have caught up. The coming surface is the empty-input exploit: a pipeline that returns success, an agent that trusts the boolean, a state transition that executes against nothing. The blast radius is larger than a single contract because the assumption is shared across every consumer in the dependency chain.

The fix is not more formatting. It is not a richer schema. It is a single require() on the preimage β€” a check that the fact base is non-empty before the analysis function is allowed to run. Clarity is the highest form of optimization, and the clearest thing a pipeline can say is "I cannot answer this." The question is whether we build systems that can say it, or systems that merely look like they did.