PostgreSQL Error 2200B: escape_character_conflict
PostgreSQL error 2200B (escape_character_conflict) occurs when the escape character specified in a LIKE or SIMILAR TO clause conflicts with the wildcard characters (% or _) used in pattern matching. This error is thrown when PostgreSQL detects that the designated escape character is ambiguous or violates SQL standard rules, making it impossible to correctly interpret the pattern. It commonly surfaces in applications that dynamically build search queries or process user-supplied input.
Top 3 Causes
1. Using a Wildcard Character as the ESCAPE Character
Specifying % or _ as the escape character directly conflicts with their reserved roles in pattern matching.
-- ❌ Bad: using wildcard '%' as escape character
SELECT * FROM products
WHERE product_code LIKE '50%_off' ESCAPE '%';
-- ERROR: 2200B escape_character_conflict
-- ✅ Fix: use a neutral character like '!' or '\'
SELECT * FROM products
WHERE product_code LIKE '50!%!_off' ESCAPE '!';
-- Matches literal string '50%_off'
-- ✅ Alternative with backslash
SELECT * FROM products
WHERE product_code LIKE '50\%\_off' ESCAPE '\';
2. Specifying a Multi-Character Escape String
The ESCAPE clause strictly requires a single character. Passing a two-or-more character string causes PostgreSQL to raise this error.
-- ❌ Bad: multi-character escape string
SELECT * FROM orders
WHERE note LIKE '%discount!!%' ESCAPE '!!';
-- ERROR: 2200B - escape string must be a single character
-- ✅ Fix: use exactly one character
SELECT * FROM orders
WHERE note LIKE '%discount\!%' ESCAPE '\';
-- ✅ Safe dynamic pattern building in PL/pgSQL
DO $$
DECLARE
v_esc CHAR(1) := '!'; -- strictly typed as single char
v_pat TEXT;
BEGIN
v_pat := '%'
|| replace(replace('50%_off', '%', '!%'), '_', '!_')
|| '%';
RAISE NOTICE 'Pattern: %', v_pat; -- outputs: %50!%!_off%
END;
$$;
3. Escape Character Conflicts in SIMILAR TO
SIMILAR TO supports SQL-standard regex syntax, where characters like |, *, (, ) carry special meaning. Using one of these as the escape character causes a conflict.
-- ❌ Bad: using regex meta-character '|' as escape
SELECT * FROM event_logs
WHERE message SIMILAR TO '%(error|warning)%' ESCAPE '|';
-- ERROR: 2200B escape_character_conflict
-- ✅ Fix: use a neutral escape character
SELECT * FROM event_logs
WHERE message SIMILAR TO '%(error|warning)%' ESCAPE '\';
-- ✅ Better: use PostgreSQL native regex operator instead
SELECT * FROM event_logs
WHERE message ~ 'error|warning';
-- ✅ Case-insensitive regex match
SELECT * FROM event_logs
WHERE message ~* 'error|warning';
Quick Fix Solutions
-
Always use neutral characters such as
!,#, or\for theESCAPEclause. - Validate escape input before injecting it into dynamic SQL.
-
Prefer
~or~*(PostgreSQL regex operators) overSIMILAR TOfor complex patterns — they are more flexible and avoid this class of errors entirely.
-- Reusable safe pattern helper function
CREATE OR REPLACE FUNCTION safe_like_pattern(p_input TEXT, p_esc CHAR(1) DEFAULT '!')
RETURNS TEXT LANGUAGE plpgsql IMMUTABLE AS $$
BEGIN
IF p_esc IN ('%', '_') THEN
RAISE EXCEPTION 'Escape char cannot be a wildcard (2200B)';
END IF;
RETURN '%'
|| replace(replace(
replace(p_input, p_esc::TEXT, p_esc::TEXT || p_esc::TEXT),
'%', p_esc::TEXT || '%'), '_', p_esc::TEXT || '_')
|| '%';
END;
$$;
-- Usage
SELECT * FROM products
WHERE name LIKE safe_like_pattern('50%_off') ESCAPE '!';
Prevention Tips
-
Standardize escape characters across your codebase — pick one (e.g.,
!) and enforce it through shared utility functions or ORM-level abstractions. Never let developers use%or_as escape characters. -
Use parameterized queries and server-side escaping together — never interpolate raw user input into a
LIKEpattern. Combine prepared statements with a sanitization function likesafe_like_pattern()above to prevent both SQL injection and escape conflicts simultaneously.
Related Errors
| Code | Name | Description |
|---|---|---|
| 22025 | invalid_escape_sequence |
Escape character followed by an invalid character in the pattern |
| 2201B | invalid_regular_expression |
Malformed regex syntax in SIMILAR TO or regexp_* functions |
| 22000 | data_exception |
Parent error class for 2200B; can be caught as a broader handler |
📖 Want a more detailed guide?
Check out the full in-depth version (Korean) on oraerror.com — includes detailed analysis, additional SQL examples, and prevention tips.
Top comments (0)