ToolXkit Icon

Tutorial · 6 min read · Updated 20 August 2026

Why your regex works in a tester but not in your code

The pattern is fine. What broke it is the layer between you and the regex engine: your language read the string first and took half your backslashes.

You build a pattern in a tester. Every match lights up exactly where it should. You paste it into your code, run it, and it matches nothing. Or worse, it matches something that is almost right. The pattern did not change. A second parser simply got to it first.

Your language reads the string before the regex engine does

In a tester you type the pattern directly into a box, and the box hands those exact characters to the regex engine. In source code you usually type the pattern inside quotes, which makes it a string literal. String literals have their own escape rules, and those are applied before the regex engine sees anything.

Take a pattern for a digit followed by a full stop:

\d\.

Put that in a Python string and the string parser sees two escape sequences. It does not know about regex. It resolves what it recognises, and what reaches the regex engine is not what you typed. The fix is to double each backslash, or to use a raw string:

re.compile("\\d\\.")   # doubled
re.compile(r"\d\.")    # raw string, what you actually want

Java has the same problem and no raw strings, which is why Java regexes are famously full of four backslashes in a row. JavaScript avoids it when you use a regex literal, such as /\d\./, and brings it back the moment you build one from a string with new RegExp("\\d\\.").

This one mechanism explains most "works online, fails in code" reports. If your pattern contains a backslash and you are constructing it from a string, look here first.

The flavour is not the same either

Regular expressions come in several dialects. The common core (character classes, quantifiers, groups, anchors) behaves the same nearly everywhere. The edges do not.

  • Lookbehind is unsupported or length-restricted in several engines, and was missing from JavaScript for years.
  • Named groups are (?<name>…) in most places and (?P<name>…) in older Python.
  • Unicode property escapes like \p{L} need a flag in some engines and do not exist in others.
  • POSIX classes such as [[:digit:]] work in grep and sed, and not in JavaScript.

The regex tester here runs JavaScript, because it runs in your browser. That makes it exactly right for patterns that will run in JavaScript. For patterns going elsewhere it is a good first check, but not the last one.

The other three causes, in order of frequency

Multiline and dotall flags. By default ^ and $ anchor to the whole string rather than each line, and . does not match a newline. A tester with the multiline flag enabled and code without it are testing different things. Check the flags match before you suspect the pattern.

The input is not what you think. The text you pasted into the tester was cleaned by the paste; the text in your program may carry a trailing newline, Windows line endings, a byte-order mark, or non-breaking spaces that look exactly like spaces. Print the string with its escapes visible before blaming the regex. Where the input arrived as JSON, running it through a validator often finds the real problem faster than staring at the pattern.

Global state. In JavaScript a regex with the g flag carries a lastIndex between calls. Reusing the same object in a loop makes it skip matches on every other pass. The bug looks random, but it happens the same way every time.

How to test the thing you are actually running

The reliable habit is to test the pattern as your program sees it, instead of as you typed it. Log the compiled pattern, or the string after escaping, and paste that into the tester. If it matches there and fails in code, the difference is flags or input. If it fails in both, the pattern really is wrong, and you found out sooner.

For a one-off cleanup where you do not need a regex in code at all, doing the replacement directly with find and replace skips the escaping layer completely. What you type is what the engine gets. And when a transformation has quietly changed something it should not have, comparing before and after shows the damage in seconds rather than after it has shipped.

One habit to keep

Write the test case before the pattern. A regex that matches your three happy examples and nothing else is easy to build and easy to get wrong. The useful cases to collect are the near misses you do not want matched. Most regexes that fail in production do not fail by matching nothing. They fail by matching slightly too much, and nobody notices until the data is already wrong.

Tools mentioned in this article

Keep reading