Incident brief
Invalid regular expression
A regular expression pattern passed to a PostgreSQL function was not valid, usually unbalanced parentheses or brackets. PostgreSQL refused to compile it.
What lands in your log
ERROR: invalid regular expression: parentheses () not balanced
In 10 seconds
- What triggers it
- Call substring(... from pattern) with a pattern containing an unmatched opening parenthesis.
- Fix
- Balance every ( and [ in the pattern with its matching ) or ].
- Proof
- Reproduced on PostgreSQL 16.14 → Calling substring with an unbalanced-parenthesis pattern was rejected with SQLSTATE 2201B, naming the exact syntax problem. The session was unaffected afterward.
Fix
What to do right now
Application-level steps for this error.
- Balance every ( and [ in the pattern with its matching ) or ].
- If you want to match a literal '(' or ')' character rather than group with it, escape it with a backslash.
- Test regex patterns against a few real sample strings before shipping them, ideally with a fixed, non-fabricated example (see the premium test).
SELECT substring('(foo)' from '\(foo\)');For this error
See this error live on the server
Run these against the affected instance to confirm the diagnosis before you act.
Invalid regular expression (2201B) is statement-local: PostgreSQL does not retain the rejected value after the statement ends. Capture the exact bound value and statement position in application/server logs, then run this targeted validation before retrying.
Compile the exact pattern in isolation
Validate the specific value/shape that can raise this SQLSTATE.
-- Replace the sample with the bound pattern.
SELECT 'probe' ~ <pattern_literal> AS pattern_compiles;Why it happens
What PostgreSQL is telling you
The mechanism behind the error, grounded in the official manual, not paraphrased.
PostgreSQL 16 Documentation, POSIX Regular Expressions
if the pattern contains any parentheses, the portion of the text that matched the first parenthesized subexpression ... is returned. You can put parentheses around the whole expression if you want to use parentheses within it without triggering this exception.Read the full section on postgresql.org →
The invalid pattern
A single unmatched '(' is not a valid regular expression, PostgreSQL's regex compiler needs every opening parenthesis matched by a closing one, so it rejects the pattern before running it against any data.Confirming the session still works
The rejected pattern didn't affect the session, the next statement ran normally.Reproduce & verify
A real, single-session PostgreSQL reproduction
A literal transcript of SQL run against a live PostgreSQL instance in an isolated lab. The commands below are exactly what was executed.
- 1Call substring(... from pattern) with a pattern containing an unmatched opening parenthesis.
- 2PostgreSQL's regular expression compiler cannot parse the pattern.
- 3SQLSTATE 2201B is reported, naming the specific syntax problem.
One client: call substring() with a pattern that has an unmatched opening parenthesis.
-- No table needed: the failure happens purely inside the regex compiler.
SELECT 'no schema required' AS setup_note;SELECT substring('foo' from '(');SELECT 1 AS session_still_alive;What PostgreSQL actually returned
setup_note
--------------------
no schema required
(1 row)ERROR: invalid regular expression: parentheses () not balanced session_still_alive
----------------------
1
(1 row)The fix (escaping literal parentheses) was tested directly against the exact string this error is usually reached for, text that legitimately contains parenthesis characters.
Without this
Above: an unescaped, unbalanced '(' is rejected with SQLSTATE 2201B.
With this, tested
Below: escaping the literal parentheses with backslashes compiles and extracts the exact text.
- A second operational test: exact SQL, raw output, measured result, and engineer notes
- Same failure, a different SQL entry point: exact SQL, output, and verdict
- A manual-grounded production interpretation of the lab result
Card required. Cancel before day 7 and you are not charged.
Connected
Everything this error touches
Every page this SQLSTATE connects to: the concept that explains it, the runbooks that fix it, the parameters you tune to prevent it, and the sibling errors it travels with. All real cross-references. Jump straight in, or open the full interactive map.
Related errors
Verification
- Last verified
- 2026-07-16 (Docker lab, PostgreSQL 16.14)
- Verification scope
- Verified against PostgreSQL 16.14 in an isolated lab environment
- Audit status
- reviewed
Went further?
Pro unlocks the second lab proof
Free page stops the bleeding. Pro adds the operational test, SQLSTATE audit, and deeper evidence, same error, more certainty.