AI hallucinating SQL: what it looks like and how to catch it
Hallucinated SQL is not always an invented column. In our own 36-run test on a demo shop, Claude invented no column or table and made no wrong join, declined what the data could not answer, and still moved the definition behind the headline number. This guide quotes the queries and answers, gives a ten-minute check you can run on any AI-generated query, and explains the architecture in which the model no longer writes the SQL.
We asked Claude how many units a demo shop sold in one week. The query ran, the join was right, and the answer was 305 units. By the shop’s own definition, completed orders only, the answer is 292. The model had counted returned orders as sold, and said so, in all three runs. That, not an invented column, is what hallucinated SQL mostly looked like in our own test.
Below are the failures from 36 runs, quoted as written, a ten-minute check for any AI-generated query, and the architecture that removes the step where they come from. One model, 12 questions, one clean demo dataset: a catalogue, not a benchmark.
Originally published July 2026. Fully updated in October 2026 with what hallucinated SQL looked like in 36 runs on our demo data, a ten-minute check, and the same questions asked through OWOX Data Marts.
Your AI analytics tool is making up numbers – and you might not know it
Your AI analytics tool is making up numbers – and you might not know it
The scariest thing about AI hallucination in analytics isn’t that it happens. It’s that the fabricated numbers look exactly like real ones.
The Reddit horror story: “AI made up analytics data for 3 months”
In late 2025 a post on r/analytics described a team whose AI tool had returned fabricated metrics for months, unquestioned until someone re-ran a query by hand.

Why this isn’t a bug – it’s an architecture problem
Most AI analytics tools turn your question into SQL on the fly: an LLM guesses the right query, runs it and hands back a number. That step is probabilistic, not deterministic. The AI doesn’t know the answer, it predicts what the right query looks like, and sometimes predicts wrong while sounding confident.

What AI hallucination actually means in analytics
What AI hallucination actually means in analytics
It is not the same as bad data quality, and the difference decides where you look.
Hallucination ≠ wrong data input – it’s fabricated output
Garbage in, garbage out is a quality problem: the source data is wrong, so the reports are wrong. AI hallucination is different. The source data can be perfectly clean and the AI still returns a number that does not follow from it. IBM notes that models hallucinate when they lack constraints. In analytics, the missing constraint is the text-to-SQL step.
The three types: invented metrics, phantom trends, confident misjoins
Three types: invented metrics, phantom trends and confident misjoins. Our test covered two. Asked for a conversion rate on a dataset with no sessions, Claude declined in 3 of 3 runs, rather than return a figure like “4.7%”. We set a trap for a wrong grain, and no run fell into it. We did not test phantom trends, patterns that are noise and vanish on a re-run.
Why traditional accuracy measures don’t catch analytics hallucinations
Precision, recall and F1 measure how well a model classifies things, like whether an email is spam. They say little about “what was our revenue last quarter?” For the scores vendors quote, see what text-to-SQL accuracy benchmarks actually measure.
OWOX Data Marts
See your first report built in real time. 15 minutes.
- Connect your data warehouse
- Pick your metrics
- Get a live Google Sheets report
In the time it takes to write a ticket. Then imagine never writing that ticket again.
Book a DemoWe'll use your actual use case
What hallucinated SQL looked like in 36 runs
What hallucinated SQL looked like in 36 runs
On clean data, Claude invented no names and made no wrong joins. What moved was the definition behind the headline number.
How we ran it
On 14 October 2026 we asked Claude 12 business questions about a demo shop’s orders, each three times. Claude had read-only SQL access through a BigQuery connector to two bare views, orders and order_lines, with no descriptions and no revenue column. We, the OWOX team, wrote the 12 questions and the hand-checked answers on the OWOX demo project, before the first run. That is why the table below is printed: so you can rerun it on your own data. We did not test messy data (pauses, gifts, partial refunds), which would add more status values and more definitions for the model to choose between. The shop’s agreed definition of revenue is completed orders only. Counts per question:
| Question (21 to 27 September 2026) | Bare views, 3 runs | OWOX Data Marts, 1 run |
|---|---|---|
| 1. Revenue | led with net, 3 of 3 | led with net |
| 2. Customers who ordered | 159 (any status), 3 of 3 | 159 |
| 3. Average order value | net in 2, cancelled excluded in 1 | net |
| 4. Units sold | 305 (cancelled excluded), 3 of 3 | 292 |
| 5. Conversion rate | declined, 3 of 3 | declined (no sessions in the two Data Marts) |
| 6. Revenue by country | declined, 3 of 3 | answered |
| 7. Gross margin % | 63.1%, net, 3 of 3 | 63.1% |
| 8. Top product category | declined, 3 of 3 | answered |
| 9. Revenue per day | net, 3 of 3 | net |
| 10. Returned orders | 4, 3 of 3 | 4 |
| 11. Revenue last quarter | net, 3 of 3 | net |
| 12. Lifetime value | three cohorts, three totals | net, 159 customers |
No invented columns, no wrong joins
No run used a column or table that does not exist, and no run’s final answer rested on a wrong join or fan-out: 0 of 36 on both counts. The trap we set for a wrong grain, averaging the price per order line instead of per order, would have given $222.05 against the correct $475.86. No run fell into it.
The three unanswerable questions were declined correctly 9 of 9 times, each naming what was missing. Asked for revenue by country, run 1 replied: “I can’t give you revenue by country, because the dataset has no country field. […] There is no customer table and no shipping or billing address, so orders can’t be tied to a country.”
A definition you did not choose
What moved was the definition. Three questions had more than one honest reading, and in every run the model chose one and said which.
| Question | What the answers led with | Other figures in the same answers |
|---|---|---|
| Units sold, 21 to 27 September | 305, cancelled excluded, in 3 of 3 runs | 292 (completed only), 349 (all) |
| Average order value, same week | $475.86 (completed) in 2 runs; $484.99 (cancelled excluded) in 1 | $484.99, $475.86, $476.96 (all) |
| Lifetime value of that week’s customers | $299,281.92, $265,761.10, $260,688: three totals | each run’s cohort, below |
Units sold is the cleanest case. The shop counts completed orders: 292 units. All three runs led with 305, adding 13 units from returned orders. Run 1 opened: “We sold 305 units between 21 and 27 September 2026 (inclusive, by order date). That counts orders with status Completed (292 units) and Returned (13 units) and excludes Cancelled orders (44 units), since a cancelled order is not a sale.” It ended: “The data has no definition of “sold”, so the 305 rests on my assumption that cancelled orders don’t count.“
The answer is correct for the definition it states, and it is the figure that reaches a slide if nobody reads past the first sentence. The SQL, in all three runs, holds no status filter: it returns units per status, and the model then added the rows it chose (run 1’s query below, dataset path removed, line breaks added).
SELECT o.status, COUNT(DISTINCT o.order_id) orders, COUNT(*) lines,
COUNT(DISTINCT l.order_line_id) distinct_lines, SUM(l.quantity) units,
MIN(l.quantity) minq
FROM orders o JOIN order_lines l USING(order_id)
WHERE o.order_date BETWEEN '2026-09-21' AND '2026-09-27'
GROUP BY o.status
Lifetime value has no definition in the data, so each run chose a cohort and wrote it down:
| Run | Cohort the model chose | Total |
|---|---|---|
| 1 | 159 customers with any order that week | $299,281.92 |
| 2 | 138 customers whose order that week was not cancelled | $265,761.10 |
| 3 | 135 customers with a completed order that week | $260,688 |
Run 3 flagged the catch: “Because the cohort is defined by the order week, every customer has at least one order in it, so this average will look higher than the LTV of customers in general.”
Errors that announced themselves
4 of the 36 runs hit a SQL error on a first attempt and fixed it before answering, one on a reserved word, the others on syntax or an unsupported correlated subquery. One noted: “This query failed with a syntax error because nulls is a reserved word.” An error that stops the query is the cheap kind. The kind that runs: one answer listed seven daily revenue rows and then “Total for the week: 64,717.21 USD.” The rows add up to 64,717.20. The answer looked as careful as the others, and only a hand total showed the cent.
The same questions through OWOX Data Marts
We asked the same 12 questions through the OWOX Data Marts connector, on the demo project’s Purchases and Orders Data Marts, one run per question. On every revenue-type question the answer led with the agreed net figure, labelled, with gross alongside. For revenue it replied: “Revenue for 21-27 September 2026 was $64,717.20 (USD). That is net revenue, meaning only Completed orders count; cancelled and returned orders are recorded as 0.”
It also answered revenue by country and the top category, which the bare views had to decline, through the Data Marts’ joins. We checked them with independent SQL on the warehouse views the connector reads, for the same week: every country and category row matched to the cent, the country rows add to $64,717.20 net and $77,267.82 gross, and the category units add to 349 (136 + 110 + 57 + 46).
The limits, plainly. With two Data Marts available it declined the conversion rate, because sessions sit in a third. For lifetime value the two had no join, so it matched them by hand; its $299,281.92 equals the bare-view run that chose the same cohort.
How to catch hallucinated SQL in ten minutes
How to catch hallucinated SQL in ten minutes
A wrong join gives a number that looks like every other number, so check the SQL and the definition, not how the answer reads. Five steps, about two minutes each:
- Read the SQL before the number. Look for names that are not in your schema, a join key that is not the one you use, and a join at a different grain from the one you aggregate. In our run every name existed, so check that each real column means what the query assumes.
- Check the status filter. Which statuses are in the sum? In the units answers the SQL held no filter, and the choice was made when the model added rows.
- Ask what was counted. In our runs every answer that chose a definition said which and listed the alternatives. Treat an answer without that line as a draft.
- Compare one figure with a hand total. Pick one week and one status, and write the total yourself or take it from the finance report. That is how the cent turned up.
- Ask something the data cannot answer. A tool that declines and names what is missing, as Claude did 9 of 9 times, is behaving. One that returns a conversion rate with no sessions is not.
The same checks apply to SQL a person wrote, because the failure is the unstated definition, not who typed the query.
On joins and grain, an earlier run of ours found the same pattern: the joins were written correctly and the definition was the open question, as set out in why AI gives different answers to the same data question.
The technical root cause – AI-generated SQL is non-deterministic
The technical root cause – AI-generated SQL is non-deterministic
The root cause is specific: text-to-SQL is probabilistic.
How text-to-SQL actually works (and where it breaks)
The pipeline looks simple: the user asks, the LLM writes SQL, the SQL runs on your warehouse, and the AI narrates the result. The hallucination vector is the second step. The LLM doesn’t look up the right query from a verified library. It generates one every time, from statistical patterns and whatever schema context it has. The same question can produce several valid queries with different numbers.

Same question, different SQL, different answer – every time
Ask “What was our revenue last quarter?” and the LLM has to decide:
- Date boundaries: calendar quarter, fiscal quarter or the last 90 days?
- Revenue definition: gross or net, with or without refunds?
- JOIN logic: which tables, on what keys, inner or left?
- Aggregation: SUM of what column, after deduplicating what?
Each decision is the model’s to make, and nothing in the schema settles it. On the clean demo data above, most choices repeated, but not all: average order value led with a different definition in 1 of 3 runs, and units sold in 3 of 3. On a harder set of 25 questions the model summed gross revenue where the company counts net in 7 of 66 runs, with no warning. You can’t audit what you can’t reproduce.
The semantic gap: business meaning vs. database schema
Terms like “active user” and “revenue” have precise definitions that differ by company, and LLMs don’t know yours. They guess from column names: a column called revenue might store gross merchandise value. What we saw when we tested that guessing on our own demo data is in can you trust AI with your business numbers? More context reduces how often it happens, but the SQL generation step remains probabilistic.
The numbers that should scare you
The numbers that should scare you
Most published AI error rates are not about SQL, so we left them out. One outside fact bears on SQL answers.
EU AI Act: audit trails are a regulatory question
Under the EU AI Act, the highest fines, up to €35 million or 7% of worldwide turnover, are for prohibited practices. Most other obligations, including a deployer’s, carry up to €15 million or 3% (Article 99). Whether an analytics tool counts as high-risk depends on its use, so ask your legal team. The practical point is the question auditors ask of any revenue figure: can you show the computation that produced it?
The fix is architectural – deterministic SQL explained
The fix is architectural – deterministic SQL explained
The fix is not better prompts or smarter models. It is taking the SQL-writing step away from the model.
AI-generates-SQL vs. AI-narrates-approved-SQL – the two architectures
| Architecture A: AI generates SQL | Architecture B: AI narrates approved SQL | |
|---|---|---|
| How it works | User question → LLM generates SQL → runs on warehouse → returns result | Analyst writes SQL → stored in Data Mart → user question → AI matches to pre-approved query → runs approved SQL → AI narrates result |
| SQL authorship | AI writes SQL from scratch each time | Analyst writes SQL once; AI never writes SQL |
| Determinism | Non-deterministic – same question can yield different queries | Deterministic – the same structured request always produces the same SQL |
| Audit trail | No – generated SQL is ephemeral | Yes – every query is logged, versioned, traceable |
| Hallucination risk | Inherent in the SQL-writing step | Lower, not zero – the AI still chooses the Data Mart and the field |
In Architecture B fixed rules generate the SQL from the AI’s structured request and record it.

How deterministic SQL works: analyst writes it once, AI explains it forever
In the Data Mart pattern, an analyst defines business metrics as SQL, views or tables.

The definitions live in governed Data Marts, versioned, reviewed and approved.

The analyst joins Data Marts to each other (lightweight modeling) with precise join logic, filters and aggregation rules.

When a user asks a question, the AI maps it to the right Data Marts, and the SQL is generated only from the request and the defined join keys. No AI is used to generate SQL; the AI’s job is narration. The same structured request on Monday and Friday runs the same SQL. The risk moves from the SQL to how the request is mapped to a definition: in our runs, asking for the “top line” made the assistant lead with gross revenue, labelled, instead of net. How often is counted in the OWOX section below.
Version control, audit trails, and reproducible results
Every metric has traceable lineage: who owns the source, which join keys were used, and when it was last modified (full version history is coming soon). Auditors need “here is the exact computation, here is who approved it”, not “an AI said so”.
Why prompt engineering and RAG don’t solve the underlying problem
Prompt engineering guides the LLM toward better SQL, but “better” is not “correct”. RAG gives it your schema documentation, which narrows the semantic gap, but the join keys to use are still not defined, and retrieval over warehouse metadata has traps of its own.
Only taking SQL writing away from the model removes that step’s errors. It does not remove every error. That’s the architectural difference.

How OWOX eliminates hallucination by design
How OWOX eliminates hallucination by design
OWOX implements Architecture B, and “zero hallucinations” is what we aim for: the model stops writing the SQL, which removes the step where our bare-view runs chose their own status filter and hit 4 SQL errors in 36 runs, but the request can still be mapped to the wrong definition, so it lowers the risk and is not a 100% guarantee. What is left is visible and checkable, and counted below.
The architecture: Data Marts with pre-approved SQL → AI narration layer
Layer 1: Joinable Data Marts. Analysts define business metrics as SQL, stored as governed, versioned Data Marts in your cloud data warehouse, each with a precise schema: what it measures, what it joins to, what filters it applies. Together they form your model, and a person writes “net revenue” once, in a place every answer reads from.
Layer 2: AI Insights. The AI layer maps each question to the right Data Marts, runs the pre-approved SQL and narrates the result. The AI never generates SQL and never touches the warehouse directly. It still has to turn your question into a structured request: which Data Mart, which fields, which filters. That choice is the assistant’s, it is visible in the request log (Run History), and it is where a wrong definition can still enter. For “how many units did we sell”, the request in our run was:
{"data_mart_id": "0212efac-2c7d-4bcb-aaf2-055229de4b75", "fields": ["quantity", "orders_e_commerce__status"], "aggregations": [{"field": "quantity", "function": "SUM"}], "filters": [{"field": "orders_e_commerce__order_date", "operator": "between", "value": {"from": "2026-09-21", "to": "2026-09-27"}}]}
It asks for units per status, with no status filter, and the assistant then chose which statuses to report (292, completed). Only the SQL step is taken away from it. In our own 25-question test on demo data (11 October 2026, Claude Sonnet 5.5, one run per question) this side answered 20 of 22 answerable questions correctly: one answer led with gross revenue where the company counts net, and one question was declined. In the 12-question run above, every revenue-type answer led with the agreed net figure, one run per question.

Patented technology: what it means in practice
OWOX’s deterministic analytics approach is patented1.
From question to answer – a deterministic pipeline walkthrough
When a CMO asks “What was marketing’s contribution to pipeline last quarter?”, the AI matches the intent and selects the right Data Mart and the ones joined to it. The analytics team’s approved SQL runs with the same date boundaries and attribution model every time, and every number links back to the Data Mart, the SQL and the source tables. Every step is logged, so the analyst can verify the SQL and the auditor can trace the lineage.
How to evaluate any AI analytics vendor for hallucination risk
How to evaluate any AI analytics vendor for hallucination risk
Evaluating OWOX or any other platform? Ask these.
The 5-question vendor checklist
The answers tell you whether the product is built on Architecture A (hallucination-prone) or Architecture B (deterministic):
- Does your AI generate SQL, or create it by deterministic rules? If the AI writes SQL, hallucination is inherent.
- Can I see the exact SQL behind every metric the AI returns? If not, you can’t audit the output.
- Will the same question return the same answer tomorrow? If not, the architecture is non-deterministic.
- Who defines business metric logic, the AI or an analyst? If the AI defines what “revenue” means, it’s guessing. If an analyst defines it, it’s governed.
- Can I audit the full lineage from question to data source? If the vendor can’t provide it, walk away.
Red flags that signal hallucination-prone architecture
- “AI writes SQL so you don’t have to” is the hallucination vector packaged as a feature.
- No audit trail or query history. If you can’t see what was computed, you can’t trust it.
- “Natural language to insights” with no word on the SQL layer.
- Demos that only show single-table queries. Ask for a multi-table join with business logic.
What “AI-powered analytics” should actually mean
AI should make analytics accessible, not unverifiable: narrating SQL-produced results in plain language, explaining why a metric changed, and flagging anomalies in data that’s already been computed correctly. Generating the queries that produce the numbers is analyst work, and AI should amplify analysts, not replace them. A separate guide covers what to check when a business user builds the dashboard themselves.
Turn your data into decisions.
Governed data marts give you the clean foundation ML needs to actually work.
- No AI hallucinations
- Analyst-governed definitions
- Every number traces to SQL
Frequently Asked Questions
What does the EU AI Act mean for AI analytics tools?
The EU AI Act sorts AI systems by risk, and whether an analytics tool counts as high-risk depends on how it is used, so ask your legal team. The highest fines, up to €35 million or 7% of worldwide turnover, are for prohibited practices; most other obligations, including a deployer's, carry up to €15 million or 3% (Article 99). The practical question auditors ask of any revenue figure is whether you can show the computation that produced it, so check that your tool keeps the SQL and the definition behind each number.
How does a semantic layer prevent AI hallucinations?
A semantic layer translates database schemas into business terms, helping AI understand what 'revenue' or 'active user' means in your organization. It reduces hallucination by providing context, but doesn't remove it if the AI still writes SQL on top of it. Having the AI send a structured request, with the SQL built from the analyst's definitions by fixed rules, takes that step away from the model. The AI can still pick the wrong field, so no setup prevents every error.
What are the business risks of AI hallucination in analytics?
AI hallucination in analytics can lead to decisions based on numbers that do not follow from the data: misallocated budgets, wrong forecasts, flawed strategy. In our own 36-run test on a demo shop, the risk was less an invented column than a quietly different definition. Asked for units sold, all three runs led with 305, counting returned orders, against 292 for completed orders, and each said so in its answer. Regulation adds a second risk: under the EU AI Act, fines run up to €35 million or 7% of worldwide turnover for prohibited practices and up to €15 million or 3% for most other obligations (Article 99). Whether an analytics tool is high-risk depends on its use.
How do you verify AI-generated analytics are correct?
Read the SQL, then check what the number counts. In our own 36-run test every answer showed its SQL, and the checks that mattered were which statuses were in the sum and one figure compared with a total computed by hand. With deterministic SQL, every metric has traceable lineage: who owns the source, which join keys were used and when it was last modified. You can inspect the exact computation behind a number and reproduce it independently.
Can you stop AI from hallucinating in analytics?
Not completely, but you can remove the step where most of it happens. If the AI never writes SQL and instead sends a structured request that runs analyst-written definitions, the SQL is built by fixed rules and recorded. The AI still chooses which Data Mart and field to use: in our own 25-question test on demo data, this setup answered 20 of 22 questions correctly, with one wrong headline figure and one question declined. Prompt engineering and RAG reduce errors in AI-written SQL but do not remove them.
What is the difference between deterministic and AI-generated SQL?
AI-generated SQL is written by an LLM each time a user asks, so the same question can produce different queries and, in our test, sometimes a different definition: average order value led with a different definition in 1 of 3 runs, and lifetime value gave three totals from three cohorts. Deterministic SQL is written once by an analyst, stored in a governed Data Mart and run the same way every time. The AI's role shifts from writing the query to explaining the result, though it still chooses which Data Mart and field to use.
Why do AI analytics tools hallucinate metrics?
AI analytics tools write SQL on the fly with a language model, and that step is probabilistic: the model predicts a likely query rather than looking up a verified one. On clean data in our own 36-run test the failure was not an invented column or a wrong join (0 of 36 on both counts) but a definition chosen quietly: for units sold all three runs led with 305, counting returned orders, against 292 for completed orders, and said so. Where the data cannot settle a definition, as with lifetime value, three runs gave three totals from three cohorts. It was one model and one clean dataset, so these are counts, not rates.
What does a hallucinated SQL query look like?
In our own 36-run test, Claude, writing its own SQL on a demo shop, invented no columns or tables and made no wrong join. It declined the three questions the data could not answer, 9 of 9 times. What moved was the definition: asked for units sold, all three runs led with 305, counting returned orders, against 292 for completed orders. Each said so in its answer. Asked for the lifetime value of one week's customers, three runs gave $299,281.92, $265,761.10 and $260,688, from three different cohorts. It was one model, 12 questions and one clean dataset, so these are counts, not rates.
How do I check AI-generated SQL?
Read the SQL before the number, and check which statuses are in the sum. Ask what was counted and what was left out, and compare one figure with a total you compute yourself. Then ask one thing the data cannot answer. In our runs the hand total exposed a total that was a cent off. Every answer that chose a definition named it, and a good answer declines and says what is missing.

