Zero-Hallucination Code Generation: A Vocabulary Your AI Cannot Escape

Zero-Hallucination Code Generation: A Vocabulary Your AI Cannot Escape

Every developer who has used AI code generation has seen it.

The model confidently calls a function that does not exist.

The code looks right. The naming is plausible. The arguments make sense. And the function was never written by anyone, anywhere, ever.

This is what hallucination looks like in code generation, and the industry's standard answer is to prompt harder. Add more instructions. Add more examples. Ask the model nicely to please only use real APIs.

I think that is backwards.

Hallucination is not a prompting problem you beg your way out of.

It is a verification problem you engineer your way out of.

In this article I will show you how Magic eliminates hallucinated functions from generated Hyperlambda. Not reduces. Eliminates. And it does it with two gates, both enforced by the runtime itself.

The first gate makes it impossible for the generator to return code referencing functions that do not exist.

The second gate makes it impossible for code to invoke functions outside a vocabulary you explicitly declared.

Together they produce a property most code generation stacks cannot claim.

The AI is free to be creative in composition. It is never free to be creative in vocabulary.

Why Hyperlambda can do what general-purpose languages cannot

To understand why this works, you have to understand one thing about Hyperlambda.

Every statement in Hyperlambda is an invocation of a function the runtime knows about.

There are no user-defined classes scattered across a hundred files. There is no unbounded import surface. There is no dynamic dispatch into code the runtime has never heard of. A Hyperlambda program is an execution tree, and every executable node in that tree is the name of a function registered in the runtime.

That means the complete set of legal functions is a closed, enumerable list.

The runtime can answer, at any moment, a question that is practically unanswerable in Python or JavaScript.

Does this function exist? Yes or no.

That single property changes everything about hallucination.

In a general-purpose language, a hallucinated API is a needle in an infinite haystack. You find out when the build breaks, or worse, when production does.

In Hyperlambda, a hallucinated function is simply a name that is not in the list.

And I should be clear about what happens if such a name ever reaches execution. It throws. Immediately. The runtime refuses to invoke a function it does not know. There is no silent failure mode, no best-effort interpretation, no pretending.

But throwing at execution time is the last line of defense.

The interesting engineering is making sure hallucinated code never gets that far.

Gate one: code that references non-existing functions is never returned

The Hyperlambda Generator is a fine-tuned model that emits raw Hyperlambda from a plain-language prompt.

Fine-tuned models are good. They are not perfect. Under pressure, any model can drift and invent a function name that feels right.

So the generator does not trust its own output.

Every completion runs through a verification pipeline before it is allowed to leave the building, and the interesting part is a function called hyperlambda.verify-slots.

It does one thing.

It walks every executable statement position in the generated code, and checks each function invocation against the registry of functions that actually exist in the runtime.

Not against a hardcoded list someone has to maintain.

Against the live registry. The same source of truth the runtime itself dispatches from.

And "every executable statement position" is doing real work in that sentence. The verifier does not just scan the root of the file. It descends into every lambda body the code declares. Loop bodies. Branch bodies. Callback bodies. Even functions that evaluate their children as code get their children checked.

How does it know where the executable bodies are?

From metadata.

Every function in Magic carries a signature declaring its structure, including where its executable bodies live. Some functions execute their own children directly. Others wrap their body inside a named child. The verifier reads that metadata and routes its traversal accordingly.

That detail matters more than it looks.

It means the verifier has no private knowledge of the language. It is not a parallel implementation that can drift out of sync with the runtime. It reads the same signatures the runtime publishes, so when a new function is added to the system, the verifier understands it automatically.

One source of truth. Zero maintenance.

What happens when the model hallucinates anyway

Here is the pipeline from the generator's point of view.

The model produces a completion. The completion is parsed. Then every function reference is verified.

If a single referenced function does not exist, the completion is rejected.

Not warned about.

Rejected.

The offending names are collected, the attempt is thrown away, and the generator retries. The first attempt runs deterministically at temperature zero. Retries explore with a higher temperature, because if the deterministic answer was wrong, we want a different answer, not the same wrong one again.

If the model cannot produce code that passes verification within its attempts, the generator fails loudly with an error.

That is the contract.

The generator either returns Hyperlambda in which every single function exists, or it returns nothing.

There is no third option where you receive plausible-looking code with a landmine in it.

This is what I mean by zero hallucination. It is not a claim about the model. Models hallucinate. It is a claim about the system. The system is structured so that hallucinated function references cannot survive the pipeline.

The model proposes.

The registry disposes.

Gate two: whitelist, the vocabulary cage

The first gate guarantees that generated code only references functions that exist.

But existing is not the same as being allowed.

Sometimes I want to execute code that I do not fully trust. Code from an AI model. Code from a user. Code assembled dynamically at runtime. For that, Hyperlambda has whitelist, and I have written about it before from a security angle.

Here I care about it from the hallucination angle.

whitelist takes two arguments. A vocabulary node declaring which functions are legal, and a .lambda node containing the code to execute.

whitelist
   vocabulary
      set-value
      return
   .lambda
      .result
      set-value:x:@.result
         .:foo
      return
         result:x:@.result

Inside that .lambda body, exactly two functions exist.

Not two functions are recommended. Not two functions are suggested by the prompt.

Two functions exist.

The enforcement does not live in some wrapper that inspects the code before running it. It lives inside the evaluator itself. Every single invocation, at the moment of dispatch, is checked against the active vocabulary. If the function is not in the vocabulary, the runtime throws before the function ever executes.

This is a much stronger statement than static analysis.

Static analysis tells you what the code appears to do.

The evaluator decides what the code can do.

Pinning arguments, not just functions

And there is a detail in whitelist that I think deserves its own section, because it takes the idea from function-level containment to capability-level containment.

A vocabulary entry can carry a value.

When it does, the value pins the argument.

whitelist
   vocabulary
      io.file.load:/etc/data/customers.csv
      return
   .lambda
      io.file.load:/etc/data/customers.csv
      return
         result:x:@io.file.load

This vocabulary does not say the code may load files.

It says the code may load this file.

If the code inside the lambda tries to invoke the file loading function with any other path, the evaluator refuses it, exactly as if the function did not exist at all.

Think about what that means for AI-generated code.

You are no longer granting the model a category of power and hoping it stays polite. You are granting it a specific, named capability. Load this file. Connect to this database. Nothing else resolves.

The hallucination surface does not just shrink.

It closes.

Even if a model drifted and produced a syntactically legal invocation of a permitted function with a hallucinated argument, the vocabulary refuses it at dispatch.

How the two gates compose

Now put the gates together, because the composition is the point.

Gate one is static. It runs at generation time and guarantees that code referencing non-existing functions is never returned to anyone. The hallucinated function name dies inside the generator's retry loop, invisible to the user.

Gate two is dynamic. It runs at execution time and guarantees that code, no matter where it came from, can only invoke the functions you explicitly placed in its vocabulary, with the arguments you pinned.

Neither gate trusts the model.

Neither gate trusts the prompt.

Neither gate trusts the code.

The first trusts the function registry. The second trusts the evaluator. Both are runtime semantics, not conventions, which means there is nothing to forget, nothing to prompt around, and nothing that degrades when the model has a bad day.

The result is a system where the AI has genuine creative freedom in how it composes a solution, while the vocabulary it composes from is bounded on both ends.

It cannot emit a function that does not exist.

It cannot invoke a function it was not given.

The bigger lesson

I think the industry is attacking hallucination at the wrong layer.

Most of the effort goes into the model. Better prompts. Better fine-tunes. Better retrieval. All of that helps, and I do plenty of it myself. But it all shares the same ceiling. You are reducing the probability of hallucination, and probability is not a guarantee.

Guarantees come from the layer below.

If your language has a closed vocabulary, hallucinated functions become detectable. If your generator verifies against that vocabulary before returning, hallucinated functions become unreturnable. If your evaluator enforces a declared vocabulary at dispatch, unauthorized functions become uninvokable.

At that point you are no longer hoping the model does not hallucinate.

You have made hallucination unrepresentable in the output.

That is the property I built for, and it is a property I believe orchestration languages get almost for free, because they already know their complete function surface. The haystack is finite. The needle has nowhere to hide.

Do not beg your model to stop hallucinating.

Build a runtime where its hallucinations cannot survive.