AELSHA™ Nehemvix Start a free guide

How to catch hallucinated function and file names in AI-generated documentation

Check every file path and code name the model writes against the repository itself, in code, before anyone reads the documentation.

AI-written documentation can name functions and files that were never written, and those names look exactly like the real ones beside them. The good news is that this kind of mistake can be caught by a program, without asking another model.

What is an AI hallucination?

An AI hallucination is output from a language model that sounds fluent and confident but is not supported by its input or by the facts: a function that does not exist, a file nobody wrote, a test result nobody ran. In documentation it is especially costly. A reader who trusts the page will search for the name, fail to find it, and then wonder what else on the page is wrong.

Why does AI hallucinate?

A language model writes the words most likely to come next, given what it has read. It does not look anything up while it writes. Where the material is thin, the most likely words are often a plausible name rather than a real one. A signing library could well have a Signer.verify_token method; whether a given library has one is a fact the model cannot check from inside the sentence.

Prompts help but do not cure it. You can tell a model to mention only files and functions present in the material, never to invent test counts or versions, and to say when it is unsure. Some invented names still get through, so you need a check after the writing.

The hallucination you can check

Most hallucinations are hard to catch. A wrong explanation of what a function does reads exactly like a right one. But one kind is mechanically checkable. When documentation says "call Signer.rotate_keys()" or "configuration lives in config/settings.py", it makes a claim about names that either exist in the repository or do not.

So check that kind in code. A program that looks names up is free, instant and reproducible, and the model being checked cannot argue with it. Save human review, or a second model, for the claims a program cannot settle.

How to check names against the code

The method has three parts, and none of them needs a model call:

  1. Build a list of what exists. Every file and folder at the commit you documented, plus a symbol index: the classes, functions and methods defined in the code. For Python, the standard ast module gives you exact definitions. Other languages can be indexed with per-language patterns, which are less exact.
  2. Pull out the claims. Take the code-like tokens the documentation writes in backticks: paths, calls, dotted names, snake_case and CamelCase names. Plain prose is left alone.
  3. Look each one up. A path is found if the file or folder exists. A dotted name such as module.function is found only if the module exists and the function is in the index.

A first version fits in a few lines:

import ast, pathlib, re

symbols = set()
for f in pathlib.Path(repo).rglob("*.py"):
    for node in ast.walk(ast.parse(f.read_text())):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
            symbols.add(node.name)
claims = re.findall(r"`([^`\n]+)`", doc)

Everything after that is refinement, and most of it comes from real misses.

Rules that came from real misses

Each rule below was written after a checker let something through or flagged something real:

What happened Rule now
Signer.rotate_keys() passed because Signer exists the last part of a dotted name must exist
URLSafeSerializer.encrypt passed because "encrypt" appears in prose the last part must be used as code
path/to/tinycalc in a guide was flagged placeholders the reader replaces are skipped
exit() was flagged language built-ins always exist
threading.Lock was read as a .lock file file extensions are case-sensitive

Built-ins need care in the other direction too: str.join exists, but str.frobnicate should be flagged.

Resolve citations in code, never trust line numbers

If your documentation links claims to code, do not ask the model for line numbers. Ask it to cite a file and a symbol, such as signer.py::Signer.sign, and resolve that to a line range yourself from the index.

What happens when resolution fails matters more. A citation that cannot be resolved should stay plain text and be listed for the reader. It should never become a precise-looking link to the wrong place.

Six names, checked one by one

Here is the method applied to documentation for itsdangerous, a small Python signing library, using names from Nehemvix's evals:

One example came from a live run. On 16 October 2026 a guide for the JavaScript utility lukeed/clsx invented an example variable, isActive. The check caught it, and the repair step described below replaced it with the real usage pattern.

How Nehemvix uses the result

Nehemvix runs this check on every guide it writes, between writing and publishing. It indexes Python exactly and a dozen other languages, such as JavaScript, Go and Rust, with patterns. The result then does two jobs.

One repair, kept only if it is better

When a section contains names the checker cannot find, the model gets one repair call that lists them: keep a name only if it is real and cite where it is defined, otherwise remove it and never invent a replacement. The repair is kept only if it has fewer unverified names than the original. Otherwise the original stays and the names are listed for the reader under "Names we couldn't find in the code".

The grounding score

Each guide gets a grounding score from 0 to 100. It weighs whether the required sections are present, how many names and citations failed to check, how much of the important code the guide covers, whether the diagram is well-formed and how readable the prose is. It is labelled "structure and grounding, not factual correctness" wherever it appears, because that is what it measures.

What this cannot catch

Be explicit about the limits, and pin them with tests so the list cannot quietly become untrue:

If the documentation you need to check wasn't written by Nehemvix, we describe the same checks as a method for any AI-written docs, with no LLM judge.

How well the check works should be measured, not assumed. In a blind round written by a reviewer who never saw Nehemvix's checker, it caught 94.3% of planted names, and in a harder second round 83.3%, without wrongly flagging a real name. The method is on how every guide is checked.

Checklist

Related reading: how these numbers are kept from slipping is in building an eval gate for an LLM app, and what a reader sees in a finished guide is in reading a guide.

Learning this to build your own agent? AELSHA™ can help you design it.

Try it on a repository you know

Free to use with your own model key.

Start a guide