Skip to content
RAG Explained Better

Why RAG Gets Numbers and Tables Wrong

Flattened tables, lost row context and unit errors: five causes of a wrong figure, the check that isolates each, and the fix that matches.

RAG gets a table number wrong when the retrievable unit lost the structure that made the cell meaningful: the column header, the row identity, the exact digits, the unit or scale note, or the continuation across pages. Amit Patriwala (Medium, 6 July 2026) states the failure in one line: the table was destroyed during ingestion, long before retrieval or generation ran. VDF AI’s private-RAG tables guide (captured September 2026) calls the same pattern a pipeline problem, not a model problem, and notes that turning up the context window does not repair a shredded row. Zou et al. (2025, arXiv:2504.01346) show why that matters at scale on MultiTableQA (57,193 tables and 23,758 questions): traditional RAG that relies on semantic similarity alone under-retrieves table evidence compared with table-aware retrieval. This failure is not a generic wrong chunk, where any irrelevant passage came back, and it is not the case where the labelled cell arrived intact and the model still invented a figure, which is hallucination. The taxonomy of why RAG systems fail places the leaf among ingestion and chunking faults that surface as a wrong number, so the first move is to read the retrieved chunk text and the extraction artifact that produced it before changing prompts or embeddings.

Why does RAG get table numbers wrong when the document is indexed?

An indexed document does not guarantee that a table cell survived as a labelled fact. Patriwala’s enterprise PDF pipeline (flatten text, split chunks, embed, store) works for paragraphs and collapses for financial statements, clinical tables, insurance forms, and multi-column reports, because those documents are built around structure rather than prose. Towards Data Science’s production RAG mistakes series (captured September 2026) opens with the same parsing failure: the PDF had a table, and the parser returned a string, so grouped row labels such as Premium under Health and Premium under Dental collide into one token stream with no grouping marker.

VDF AI describes the retrieval geometry that follows. A shredded financial row such as Q3 | 14,204 | 3.1% | (812) still embeds near almost any finance question because it is dense with the right vocabulary, so the fragment retrieves well and reads badly: high recall with no meaning. Zou et al. (2025) measure the research form of that gap on MultiTableQA. Their T-RAG framework reports accuracy gains of 1.2 to 11.4 percent and recall gains of 1.5 to 12.5 percent against table-retrieval baselines, and an average downstream LLM improvement of 11.2 percent over the strongest corresponding baselines across the models they evaluated. Traditional RAG and ColBERT-style baselines performed poorly in their comparison, which they read as evidence that semantic similarity alone is insufficient for table corpora. Once the document is known to be indexed and the answer is still wrong, the practical question is which of five structure failures fired on that query.

How do you tell which of the five table causes fired?

Five distinct causes produce the same wrong-number symptom, and each has a different fix, so a stage-by-stage walk that always starts at extraction wastes time when the real fault is a missing unit token beside an intact digit. KX Systems’ table-heavy RAG guide (captured September 2026) stage-walks precise extraction, contextual enrichment, format standardization, and unified embeddings. VDF AI names three pipeline causes: the chunker destroys the table, some questions are not retrieval questions, and spreadsheets behave like applications. Neither ships one table where each row is a discriminating check that names the cause from a single labelled failure.

Read the table as a triage card for one failing query where you already know the gold cell. Fleet meters such as document-level recall will flatter a system that retrieved the right PDF and still answered from the wrong row. Worked retrieval formulas live with evaluation elsewhere; here the deltas are pass or fail on header presence, row identity, digit fidelity, unit tokens, and continuation integrity.

Five causes of a wrong table number, the check that isolates each, and the matching fix
Cause What you see Detection (run this first) Fix
1 · Header loss Right digits; missing or wrong column label header_preservation Carry headers in every row chunk
2 · Wrong row Cites the right document; value from another row row_hit@k Structure-aware row chunks
3 · Digit / OCR corruption Numeric string does not match the source cell digit_match(source, chunk) Fix extraction first
4 · Unit / scale error Magnitude looks right; unit, scale, period, or footnote wrong unit_match + scale check Serialize unit with the value; refuse bare numbers
5 · Multi-page / merged loss Continuation rows orphaned; merged label not propagated continuation_integrity Merge before chunking

header_preservation asks whether the gold cell’s column header and a table or section title appear in the same chunk as the value. row_hit@k asks whether top-k contains the exact answer-bearing row id, not merely the right document. digit_match compares the normalized numeric string in the chunk to the source cell. unit_match requires unit, scale, or period tokens from the header or legend in that same chunk. continuation_integrity requires carried-forward headers when the gold row sits on a continuation page or under a merged parent label. The first cause to expand is header loss, because an orphan number passes most document-level checks while remaining unusable as an answer.

Did the retrieved chunk lose the column header?

A retrieved number without its column label is not an answer; it is an orphan value. Patriwala’s clinical example shows the failure after flatten-and-chunk: asking what grade Sarah received fails when one chunk holds SarahBCR and another holds Grade1David, because the relationship between Sarah and Grade equals 1 no longer exists. VDF AI’s finance row is the same geometry: without the header row the figures are meaningless, and without the units note they are ambiguous between thousands and millions.

Symptom

The system retrieves a bare magnitude or status token while dropping the column that named response SLA versus refund window, Health Premium versus Dental Premium, or actual versus forecast. Towards Data Science’s Plan B Premium collision is the grouped-header form of the same symptom.

Detection

For the gold cell, assert that the column header or concatenated multi-tier header and a table or section title appear in the same retrieved chunk as the value. Measure header_preservation: 1 if present, 0 if absent. Document-level recall can still look fine.

Cause

Flattening, stream extraction, or a fixed token window severed the header row from the body, so the retrievable unit no longer names what the number means. note.com’s PDF and tables guide (4 April 2026) flags two-tier headers and units written only on the header side as the common accident where data remains while headers stay ambiguous.

Fix

Repeat the minimum headers inside every row-level unit. How to build those chunks is chunking tabular data; how to keep structure out of the parser is handling tables and structured data.

Parth Sakpal’s LinkedIn walkthrough of tables versus text in RAG (captured September 2026) describes the legacy flatten format and the issues that follow when newer models still receive misaligned headers and values. Once headers travel with every row, the next failure to rule out is retrieving a neighbour row from the same document.

Why does RAG cite the right document but return the wrong row?

Document-level recall can pass while the answer-bearing row never enters the context window. That is why generic retrieval metrics under-detect table failures: they score the page, not the row. Patriwala’s point that embeddings are not relational databases applies here: once row relationships disappear, the right PDF can still supply the wrong entity’s value. VDF AI states the user-visible form as an answer that comes back wrong, right for the wrong period, or confidently attached to the wrong row.

Symptom

The answer cites the correct PDF or workbook, but the value belongs to a neighbouring row or period; sibling chunks compete across runs; the model stitches partial rows into a plausible unsupported figure. Sakpal documents tables split across chunks as a named failure under reformatted DataFrames that still get cut mid-table.

Detection

Measure row_hit@k: did top-k include the exact answer-bearing row id? If document hit is 1 and row hit is 0, stop blaming the embedding model. The unit of retrieval is wrong.

Cause

Rows were blended or split at extraction, or a token-count splitter cut mid-row so no single chunk holds entity and value together. AWS’s Dev.to retrieval-failures walkthrough (captured September 2026) shows the general form as chunks that are too small: retrieval finds nearby headings while the answering facts never enter top-k.

Fix

Chunk by logical rows with headers carried into each unit under structure-aware table chunks. Raising top-k alone is a diagnostic, not a fix. When the query hinges on an ID, plan name, or literal SKU, add a lexical channel via hybrid search. Weaviate’s hybrid search documentation (as of September 2026) combines vector search with keyword BM25F in one query; the same fusion job appears across Weaviate, Qdrant, Elasticsearch, OpenSearch, and Azure AI Search under their own hybrid or RRF APIs. A pure mid-table cut where no returned chunk is even a near neighbour is also owned by when the answer is split across two chunks.

When the gold row text is present and the numeric string inside it still disagrees with the source cell, the failure has moved from row selection into digit corruption. That check belongs to OCR and parser fidelity, not to another top-k widen, because ranking cannot restore a digit the extraction artifact already changed.

Did OCR or parsing change the digits in the cell?

If the chunk’s numeric string does not match the source cell, retrieval never had a true fact to return. No amount of prompt tuning recovers a digit the parser already corrupted. Towards Data Science treats the string-not-table path as the root of later chunk-size tuning that cannot recover precision: every lever the team measures sits on top of the parser’s output, and a flattened table on page 47 produces noise no chunker can repair.

Symptom

IDs, SKUs, or percentages are misread; decimal points, currency symbols, or inequality signs disappear; headers and cells are read in the wrong order because layout confused the extractor. note.com separates image-based tables from regular ones: tables that look like tables but are images need OCR and layout estimation rather than a text-layer table tool.

Detection

Run digit_match: normalize the numeric string in the chunk and compare it to the source cell. For Excel, compare to the computed value when the workbook is opened with formulas evaluated. A mismatch means extraction, not ranking.

Cause

OCR noise on scans, wrong PDF table mode, or a convenience extract_text path that threw away cell bounding boxes. VDF AI places scanned tables one layer earlier: OCR quality and layout recovery decide whether structure recognition has anything to work with.

Fix

Repair extraction before embeddings or prompts. The recipes for structure-preserving extract live under table ingestion.

When the digits match the source cell and the answer still misstates the quantity, look next at the unit, scale, period, or footnote that never entered the chunk beside the number.

Is the number right but the unit or scale wrong?

RAG often returns a magnitude that matches a cell while dropping the unit, scale note, period, or footnote that changes its meaning: in millions, unaudited, currency, per-share versus total, monthly versus quarterly. That is a distinct failure from a wrong digit. The characters look plausible; the quantity is not. note.com names dropping units and periods as the most dangerous remaining pitfall after teams believe they have handled tables, because a plausible bare number is unusable in practice.

Symptom

The answer says 500 when the table said $500 in millions; mixes a quarterly column with an annual one; or omits a footnote that qualifies the row. KX Systems’ enrichment example serializes segment information with the explicit parenthetical (In millions, Unaudited) beside the markdown table for exactly this reason. VDF AI flags the units note that distinguishes thousands from millions and local currency from reporting currency.

Detection

Require unit_match: unit, scale, or period tokens from the header or legend must appear in the same chunk as the number. Flag any answer that emits a bare number when the source cell is labelled.

Cause

Serialization omitted units, or the model renormalized a labelled quantity into a bare figure during generation.

Fix

Serialize header, unit, and value as one mini-record at ingest under preserve structure at ingest. Instruct abstention on bare numbers. Do not ask the LLM to rescale or total rows in prose. When the evidence was intact and the model still invents a scale, that is when generation invents a figure, not this cause.

Even with units carried on every row, continuation pages and merged parent labels can still orphan the fact that answered the question. A second-page row that lost its carried header fails the same way a bare number fails: the magnitude looks right while the label that made it usable never entered the prompt.

Did a multi-page or merged-cell table lose the fact?

When a table continues across pages or a label spans merged cells, continuation rows often arrive without headers and child rows lose the parent label. Humans read the carry-forward; page-wise extractors usually do not. Patriwala lists merged cells and parent-child relationships among the structures embeddings cannot reconstruct after flatten. VDF AI’s structural fix explicitly recovers merged cells and multi-level headers before serialising each row with its own context.

Symptom

Page two repeats partial headers or none; a footnote that changes the row lands far from the cell in flattened text; nested tables or sidebar chrome interleave with the body; two-tier headers such as Sales under FY2024 collapse into ambiguous column names (note.com).

Detection

For a gold row on page greater than 1, or under a merged parent, measure continuation_integrity: carried-forward headers and merged-label propagation must be present in the chunk. If they are absent, merge logic failed before retrieval.

Cause

Page-wise extraction without continuation detection, or a sheet treated as one grid so merged blanks quietly shift columns (VDF AI on spreadsheets as applications).

Fix

Detect continuation, merge logically, then build row-aware chunks. merge tables before chunking owns the merge; row-aware table chunks owns the retrievable unit after merge.

Some wrong numbers never came from a single cell at all. Before spending another week on chunking, ask whether the question was a retrieval question: aggregates, trends, and cross-table totals often need a query tool rather than another embedding pass over shredded rows.

When is a table question not a retrieval problem at all?

A point lookup about a specific figure in a specific document is a retrieval question. An aggregate, trend, or cross-period comparison often is not, because the answer exists in no single chunk and must be computed. VDF AI’s second cause states that design answer as routing: point lookups go to the document index; aggregates go to a query tool over the system of record. On the BIRD text-to-SQL benchmark (12,751 question-SQL pairs across 95 databases in 37 professional domains), VDF cites leading published systems in the low eighties for execution accuracy against a measured human benchmark of 92.96 percent, which is why a generated query should be treated as a draft shown beside returned rows.

Patriwala draws the same line for row and column lookups versus narrative summarisation: questions such as the value in row 14, column 3 need table retrieval or structured lookup, not the same vector index that serves discussion-section summaries. Zou et al. (2025) build MultiTableQA around answers distributed across multiple tables, with single-hop and multi-hop table QA plus table-based fact verification, which is another signal that multi-table arithmetic is a different problem class than one-cell retrieval. Full dual-store and TableRAG architecture depth belongs with structure-preserving extract. For a failing point lookup that should have been retrieval, the next move is to run the five checks as one cause-namer.

How do you run the five table checks on one failing query?

Rather than reason about the five causes one at a time, run them against one labelled failure. You need the failing query, the gold coordinates (document, table, row, column header, value, unit), the retrieved chunk texts, and the source cell string. The function below prints which cause fired. It depends only on string checks and your top-k row ids; no new model is required. VDF AI’s evaluate-tables-separately rule matches the same instrumentation: score whether the right table was retrieved, whether the right row and column were identified, and whether the number was reported with its correct unit and period, because a pipeline that gets the first two right and the third wrong is the dangerous case.

Store row_id, column names, header-carried flags, and extraction artifact version as metadata on the same Weaviate objects you retrieve in production, or on whatever store serves the table chunks, so the printed cause matches live behaviour. Keep the failing query in a labelled table slice so the checks run in CI under RAG regression testing.

Calibrate on your corpus

Document-level recall will flatter you; row-hit and header preservation will not. The numeric cutoffs inside a local wrapper around this script are starting points for one labelled failure, not universal constants.

diagnose_table_number.py
python 3.11
import re

def _norm_num(s: str) -> str:
    """Keep digits and one decimal point for digit_match."""
    s = s.replace(",", "")
    m = re.search(r"-?\d+(?:\.\d+)?", s)
    return m.group(0) if m else ""

def diagnose_table_number(chunks, *, gold, top_row_ids, k=5):
    """Print which table-number failure fired for one labelled query.

    chunks       -> list[str]  retrieved chunk texts (top-k order)
    gold         -> dict with keys:
                    row_id, col_header, value, unit, page, merged_parent
                    source_cell (raw string from the PDF/Excel cell)
    top_row_ids  -> list[str]  row ids aligned with chunks (or [] if unknown)
    """
    text = "\n".join(chunks)
    header = gold["col_header"].lower()
    unit = (gold.get("unit") or "").lower()
    source = gold["source_cell"]

    # Cause 1: header loss: value region without its label
    if header and header not in text.lower():
        print("CAUSE 1 header loss -> carry column headers into every row chunk")

    # Cause 2: wrong row: document may be present; answer row is not
    if top_row_ids and gold["row_id"] not in top_row_ids[:k]:
        print(f"CAUSE 2 wrong row (row_hit@{k}=0) -> structure-aware row chunks")

    # Cause 3: digit / OCR corruption
    src_n = _norm_num(source)
    if src_n and src_n not in text.replace(",", ""):
        print("CAUSE 3 digit/OCR mismatch -> fix extraction before embeddings")

    # Cause 4: unit / scale missing beside the number
    if unit and unit not in text.lower():
        print(f"CAUSE 4 unit/scale missing ({gold['unit']!r}) -> serialize unit with value")

    # Cause 5: continuation / merge loss
    needs_cont = int(gold.get("page") or 1) > 1 or bool(gold.get("merged_parent"))
    if needs_cont and header and header not in text.lower():
        print("CAUSE 5 continuation/merge loss -> merge multi-page tables before chunking")

    print("done: delete ruled-out causes; fix the first that remains")

Each print line names a row of the diagnosis table. After a cause is confirmed, change only that lever, then re-run the golden pair so the same wrong figure cannot return silently.

A wrong table number is almost never a mysterious model failure. It is one of five structure faults (lost headers, wrong row, corrupted digits, missing units, or broken continuation) or a question that should have been routed to computation rather than retrieval. Inspect the retrieved table chunk and the extraction artifact first, run the five discriminating checks on a labelled gold cell, keep that failure in a table-specific eval slice, and send mechanism depth to ingestion and table chunking only after the cause is named.