Cause 1 — Duplicate keys in the lookup column (most common)
VLOOKUP returns the first match it finds. If your lookup column has accidental duplicates, lookups for that key return one specific row forever, regardless of which row you actually wanted.
Find them: in a helper column, run =COUNTIF(A:A, A2). Conditional-format any cell > 1 in red. For a one-shot check across the column:
Result is the count of duplicates. Zero is clean.
Fix: deduplicate the source data, or switch from VLOOKUP to QUERY / FILTER so all matching rows surface and the ambiguity is visible.
Cause 2 — Mixed data types in the key column
Numeric IDs imported from CSV often arrive partly as numbers and partly as text strings. 1234 and "1234" look identical on the screen but VLOOKUP does not match them.
Find them: on the lookup key column, run:
The result is the count of non-numeric cells. If you expect all numbers, anything > 0 is your bug.
Fix: coerce both sides to the same type. =VLOOKUP(VALUE(F2), A:B, 2, FALSE) converts the lookup key to a number. Or normalize the column with =VALUE(A2) in a helper column.
Cause 3 — range_lookup defaulted to TRUE
The fourth argument of VLOOKUP controls match behavior:
FALSE— exact match (what you almost always want).TRUEor omitted — approximate match. Returns the largest value less than or equal to the lookup key, assuming the range is sorted ascending. If you forgot the fourth argument, you got this.
Find them: Find & Replace (Ctrl+H) with "search within formulas" enabled, search for VLOOKUP(. Inspect each formula's argument count.
Fix: always pass FALSE as the fourth argument. =VLOOKUP(A2, Products!A:C, 3, FALSE).
Cause 4 — Hidden whitespace or invisible characters
Data pasted from email, scraped from web, or imported from CSV often arrives with trailing spaces, tab characters, or non-breaking spaces. "CUST-001" and "CUST-001 " are different strings to VLOOKUP. Looks identical to your eye.
Find them:
Returns TRUE for any cell with leading or trailing whitespace. Run as a helper column with conditional formatting.
Fix: wrap both sides in TRIM and CLEAN. =VLOOKUP(TRIM(F2), ARRAYFORMULA(TRIM(A:B)), 2, FALSE). Or normalize the source columns with a one-shot =ARRAYFORMULA(TRIM(CLEAN(A2:A))) in a helper column.
Cause 5 — Wrong column index (off by one)
You meant to return column C (the third column). You wrote 2. The formula succeeds and returns column B for every match. No error fires — just wrong data.
Find them: manually audit. =VLOOKUP(A2, X:Z, n, FALSE) — count the columns in the range and confirm n points at the right one. There is no formula-bar way to detect off-by-one.
Fix: rewrite to use INDEX/MATCH instead, which references the column by name or range:
Or upgrade to XLOOKUP, which takes the return range explicitly and is immune to off-by-one indexing.
Cause 6 — The data range silently shifted (schema drift)
Someone inserted a new column before "Price". Your =VLOOKUP(A2, Products!A:C, 3, FALSE) still computes a number, but column 3 in the new layout is "Tax Rate", not "Price". No error. Wrong number.
This is the worst class of bug because it cannot be detected by inspecting the formula alone — you have to compare the current column layout to what it was when the formula was written.
Find them: manual auditing requires keeping a locked reference sheet of the original layout. Most teams do not. The reliable approach is a tool that snapshots the schema each session and surfaces every formula whose target column moved or was renamed.
Diagnostic checklist (paste this into a helper tab)
Run these against a suspect lookup column and see what lights up:
Any non-zero result is a real defect that VLOOKUP will silently respect. Fix at the source-data level rather than wrapping every VLOOKUP in repair logic.
Scan your sheet for lookup defects
SheetBrain's Check tab (Detector Set v0.4) flags duplicate lookup keys, mixed-type columns that affect VLOOKUP type-matching, visible formula errors like #N/A, formula pattern outliers, missing copied formulas, and lookup formulas whose column index exceeds the referenced range — free, no AI quota, unlimited. Schema drift after a column move is handled separately by Repair on the $9/mo Pro plan.
TL;DR
- Duplicate keys —
=COUNTIF(col, cell)in a helper column. Caught by SheetBrain'sduplicate_lookup_keydetector in Check. - Mixed types —
=COUNTA(col) - COUNT(col). Caught by SheetBrain'smixed_text_numberdetector in Check. - Approximate match — pass
FALSEas the fourth argument always. Manual fix; not a Check detector in v0.4. - Hidden whitespace — wrap with
TRIMon both sides. Manual fix; not a Check detector in v0.4. - Wrong column index inside the range (off-by-one) — switch to
INDEX/MATCHorXLOOKUP. Manual fix. SheetBrain'sbroken_referencedetector flags the related case where the index exceeds the range. - Schema drift (column moved or renamed) — SheetBrain's Check tab does not detect this; the Repair tab does. Repair compares the active sheet to a captured schema snapshot and proposes safe rewrites.