=FormulaProof
1

Finding ·

Google Sheets will not run a regex on a date, and the obvious fix hands your pattern 46213

2

You have a column of dates. You want the rows from 2026. You write the obvious thing:

=REGEXMATCH(A2, "^2026")

The cell returns #VALUE!. Hover over it, and Google tells you something it has no obligation to tell you, and which is the most useful sentence in this entire article:

Function REGEXMATCH parameter 1 expects text values. But ‘46213’ is a number and cannot be coerced to a text.

46213. That is your date. Not 2026-07-10 — that is a costume the column is wearing. Underneath, a date in Google Sheets is a count of days from 30 December 1899, and today’s is 46213.

What we ran

Thirty-eight formulas, in a live Google Sheet. Dates, times, percentages, currency, booleans, employee ids with leading zeros, a sixteen-digit invoice number, one third, and the number 5.

We expected to find that the regex functions quietly stringify a number and match against the digits. They do not. They refuse.

Sheets refuses, and that is the good news

Every one of these returns #VALUE!:

=REGEXMATCH(DATE(2026,7,10), "2026")     the date column
=REGEXMATCH(50%, "50")                   the percentage column
=REGEXMATCH(1240, "^\$")                 the currency column
=REGEXMATCH(007, "^0")                   the employee id
=REGEXMATCH(TRUE, "TRUE")                a checkbox
=REGEXMATCH(5, "\.")                     the number five

REGEXEXTRACT and REGEXREPLACE refuse the same way. There is no coercion, no guessing, no partial credit. If the parameter is not text, the function stops.

Readers of this site will notice that this is unusual. Most of what we measure is Sheets returning a wrong answer in silence. Here it declines to answer at all, and it names the value it was handed while doing so. If it stopped here, this would be a page about a well-designed error message.

Then you fix it

The repair is one you have already thought of, and it is in every answer to this question you will find. Make the value text by concatenating an empty string:

=REGEXMATCH(A2 & "", "^2026")

No more #VALUE!. The formula runs. It returns:

FALSE

For every row. In 2026, in 2025, in any year. Because A2 & "" does turn the value into text — the text 46213. There is no 2026 anywhere in it, and there never was. The refusal has become a wrong answer with no warning, and it is now wearing the same face as an honest FALSE.

What & "" actually hands your pattern

We ran each one. The left column is what the cell displays. The right column is the string the regex receives.

the cell shows the pattern gets
2026-07-10 46213
50% 0.5
$1,240.00 1240
007 7
5.00 5
09:30 0.395833333333333
TRUE TRUE

Read that column again with a pattern in mind.

=REGEXMATCH(pct & "", "50") is FALSE on a cell that says fifty percent. =REGEXMATCH(amount & "", ",") is FALSE on a cell that says $1,240.00, because the comma belongs to the format. =REGEXMATCH(id & "", "^0") is FALSE on the employee whose badge says 007. And =REGEXMATCH(price & "", "\.") is FALSE on a cell displaying 5.00, because the two zeros after the point are a format instruction, not digits.

Then there is the one that is TRUE by accident:

=REGEXMATCH(TIME(9,30,0) & "", "9")   →   TRUE

Half past nine in the morning is 0.395833333333333 of a day, and that happens to contain a 9. The formula is asking whether the time contains a nine. It says yes. It is right, for reasons that have nothing to do with nine o’clock, and it will keep being right until someone changes the meeting to half past ten.

How much of a number does a pattern get to see

Two small mercies and one trap, since we had the sheet open.

=REGEXEXTRACT(1/3 & "", ".+")           →   0.333333333333333
=REGEXEXTRACT(0.0000001 & "", ".+")     →   0.0000001
=REGEXMATCH("", "^$")                   →   TRUE

One third arrives with fifteen digits after the point, however many the column is wide enough to show. If you are matching ^0\.33$ against a computed ratio, you are matching against a string with thirteen more characters in it than you think.

A very small number stays in decimal notation rather than turning into 1E-07, which is a mercy: a pattern like ^\d+\.\d+$ survives it. Do not assume that generalises to every magnitude without testing yours.

And an empty cell is already text. REGEXMATCH does not refuse it, so ^$ is a legitimate way to ask whether a cell is blank — one of the few places a regex is the simple answer.

The one that destroys data

=REGEXEXTRACT(1234567890123456 & "", ".+")   →   1234567890123450

Look at the last digit.

A sixteen-digit number — an invoice reference, a bank code, a card number, an order id from a system that had no reason to expect a spreadsheet — loses its sixteenth digit on the way into a string. Google Sheets carries fifteen significant digits, and the sixteenth becomes a zero.

=REGEXMATCH(1234567890123456 & "", "3456$")   →   FALSE

The number does not end in what it ends in. If a column of these was ever typed as numbers rather than text, the damage is already done, and it happened before any regex was involved. Every long id you paste into a spreadsheet should be text before it is anything else.

What actually works

TEXT(). It takes the value and the format, and it produces the string a person would read.

=REGEXMATCH(TEXT(A2, "yyyy-mm-dd"), "^2026")            →   TRUE
=REGEXMATCH(TEXT(B2, "hh:mm"), "^09:30$")               →   TRUE
=REGEXMATCH(TEXT(C2, "$#,##0.00"), "^\$1,240\.00$")     →   TRUE

All three measured, all three TRUE. & "" asks the value what it is. TEXT() asks it what it looks like. Those are different questions, and a regex is a question about appearance.

What to do on Monday

When a regex says #VALUE!, read the message. It contains the number your column is actually made of. That number is the entire explanation, and it will be a five-digit integer if you are looking at a date.

Never repair #VALUE! with & "". It silences the error without answering the question. Use TEXT(value, format) with the format you want to match against, and now the pattern and the cell agree about what is being matched.

Type long ids as text before they arrive. Format the column as plain text first, or prefix with an apostrophe. Once a sixteen-digit reference has been stored as a number, no formula can get the sixteenth digit back, because it is not there.

Ask whether you need a regex at all. For dates, YEAR(A2) = 2026 is a comparison between two numbers, which is what the data already is. Reaching for text-matching on a numeric column is how you end up here.

Every formula quoted above was executed in a live Google Sheet before this was published, and the values are the values the cells returned — including the error message, which is Google’s own wording. The full run, with timestamps, is on the evidence page.

3

The run

Nothing above was typed from memory. Here is every formula, and the value Google returned.

Execution log

REGEXMATCH does not see what the cell displays

Ran
Engine
Google Sheets (live, via Sheets API v4)
Locale
en_US
Host
Windows_NT 10.0.26200 · Node v24.15.0
Result
38 of 38 as expected
Cell Formula What Google returned
B1 =REGEXMATCH("2026-07-10","^2026")

control: a text cell is the text you see

Typed as text, or imported as text. This is the case that works, and it is why the problem is confusing.

TRUE
B2 =REGEXMATCH(DATE(2026,7,10),"^2026")

the same date, entered as a date

The cell reads 2026-07-10. The pattern is handed something else.

#VALUE!
B3 =REGEXEXTRACT(DATE(2026,7,10),"\d+")

what the pattern is actually handed

#VALUE!
B4 =REGEXMATCH(DATE(2026,7,10),"2026")

so does the year appear anywhere in a real date

#VALUE!
B5 =REGEXMATCH(TEXT(DATE(2026,7,10),"yyyy-mm-dd"),"^2026")

the fix: hand the pattern a rendering, not a value

TRUE
B6 =REGEXMATCH(50%,"50")

a percentage cell is a fraction

The cell displays 50%. Half of one is 0.5.

#VALUE!
B7 =REGEXEXTRACT(50%,".+")

what a percentage looks like to a pattern

#VALUE!
B8 =REGEXMATCH(1240,"^\$")

currency is a number wearing a costume

Formatted as $1,240.00 in the sheet. The dollar sign and the comma belong to the format, not to the value.

#VALUE!
B9 =REGEXMATCH(1240,",")

and the thousands separator is not there either

#VALUE!
B10 =REGEXMATCH("007","^0")

a leading zero survives only in text

TRUE
B11 =REGEXMATCH(007,"^0")

the same employee id, entered as a number

#VALUE!
B12 =REGEXMATCH(5,"\.")

does a whole number carry a decimal point

#VALUE!
B13 =REGEXMATCH(ROUND(5,2),"\.")

does a number formatted to two places carry one

ROUND(5,2) displays as 5.00 in a currency column.

#VALUE!
B14 =REGEXEXTRACT(1/3,".+")

one third, handed to a pattern

How many digits does a pattern get to see? The cell shows however many the column is wide.

#VALUE!
B15 =REGEXEXTRACT(1234567890123456,".+")

a very large number

#VALUE!
B16 =REGEXEXTRACT(0.0000001,".+")

a very small one

If Sheets renders this in scientific notation, ^\d+$ stops being true of a number.

#VALUE!
B17 =REGEXMATCH(TRUE,"TRUE")

is a boolean a word

#VALUE!
B18 =REGEXMATCH(TRUE,"^1$")

is a boolean a number

#VALUE!
B19 =REGEXMATCH("","^$")

what an empty cell becomes

TRUE
B20 =REGEXMATCH(TIME(9,30,0),"9")

a time is a fraction of a day

#VALUE!
B21 =REGEXMATCH(TEXT(TIME(9,30,0),"hh:mm"),"^09:30$")

the same time, rendered first

TRUE
B22 =REGEXMATCH(TEXT(1240,"$#,##0.00"),"^\$1,240\.00$")

the general fix, on a currency column

TEXT() renders the value the way the format would, and hands the pattern a string.

TRUE
B23 =REGEXMATCH(DATE(2026,7,10)&"","^2026")

the workaround everyone recommends

Concatenating an empty string makes the value text. It does not make it the date you can see.

FALSE
B24 =REGEXEXTRACT(DATE(2026,7,10)&"",".+")

what &"" actually hands the pattern

46213
B25 =REGEXEXTRACT(50%&"",".+")

a percentage, coerced

0.5
B26 =REGEXMATCH(50%&"","50")

a percentage, coerced, asked whether it is fifty

FALSE
B27 =REGEXEXTRACT(1240&"",".+")

currency, coerced

The dollar sign and the comma were never in the value.

1240
B28 =REGEXMATCH(007&"","^0")

the employee id 007, coerced

FALSE
B29 =REGEXMATCH(ROUND(5,2)&"","\.")

5.00 in a currency column, coerced, asked for its decimal point

FALSE
B30 =REGEXEXTRACT(1/3&"",".+")

one third, coerced

How many digits does the coercion keep?

0.333333333333333
B31 =REGEXEXTRACT(1234567890123456&"",".+")

a sixteen-digit number, coerced

1234567890123450
B32 =REGEXEXTRACT(0.0000001&"",".+")

a very small number, coerced

Scientific notation would make ^\d+$ false of a number.

0.0000001
B33 =REGEXEXTRACT(TRUE&"",".+")

a boolean, coerced

TRUE
B34 =REGEXMATCH(TIME(9,30,0)&"","9")

9:30 in the morning, coerced, asked whether it contains a nine

A fraction of a day. Whether it contains a 9 is an accident of arithmetic.

TRUE
B35 =REGEXEXTRACT(TIME(9,30,0)&"",".+")

9:30 in the morning, coerced

0.395833333333333
B36 =REGEXEXTRACT(1240,"d+")

REGEXEXTRACT refuses a number the same way

#VALUE!
B37 =REGEXREPLACE(1240,"d","x")

REGEXREPLACE refuses a number the same way

#VALUE!
B38 =REGEXMATCH(1234567890123456&"","3456$")

the sixteenth digit of an id number

A card number, an invoice id, a bank reference. Entered as a number, not as text.

FALSE

Every row above was written into a real Google Sheet and read back through the Sheets API on 2026-07-26. The case file and this log are in the repository, and the build refuses to run if they disagree.

4