PostgreSQL Error 22019: Invalid Escape Character
PostgreSQL error 22019 (invalid_escape_character) occurs when an invalid escape character is specified in a LIKE or SIMILAR TO pattern using the ESCAPE clause. The escape character must be exactly one single character — anything else, including multi-character strings or empty strings, triggers this error. This is a common pitfall when building dynamic SQL queries or handling user input in pattern matching operations.
Top 3 Causes and Fixes
1. Specifying More Than One Character in the ESCAPE Clause
The most frequent cause. PostgreSQL strictly enforces that the ESCAPE clause receives exactly one character.
-- ERROR: invalid escape character (two characters '\\')
SELECT * FROM products
WHERE name LIKE '%100\%%' ESCAPE '\\';
-- FIXED: use a single character
SELECT * FROM products
WHERE name LIKE '%100\%%' ESCAPE '\';
-- FIXED: use any single character as escape
SELECT * FROM products
WHERE name LIKE '%100!%%' ESCAPE '!';
2. Using an Empty String as the ESCAPE Character
Some developers try to disable escaping by passing an empty string to ESCAPE. PostgreSQL does not allow this and will throw error 22019.
-- ERROR: empty string is not a valid escape character
SELECT * FROM orders
WHERE description LIKE '%discount%' ESCAPE '';
-- FIXED: simply omit the ESCAPE clause if you don't need escaping
SELECT * FROM orders
WHERE description LIKE '%discount%';
-- FIXED: or use a regex operator which has no escape clause concept
SELECT * FROM orders
WHERE description ~ 'discount';
3. Dynamically Generated ESCAPE Characters from Application Layer
When building dynamic SQL queries in application code or stored procedures, the escape character can accidentally become malformed — especially when ORM frameworks or string concatenation logic inserts unexpected values.
-- Safe dynamic LIKE search using a helper function
CREATE OR REPLACE FUNCTION safe_like_search(search_term TEXT)
RETURNS SETOF products AS $$
DECLARE
safe_pattern TEXT;
BEGIN
-- Escape special LIKE characters: %, _, and the escape char itself
safe_pattern := '%'
|| replace(replace(replace(search_term, '!', '!!'), '%', '!%'), '_', '!_')
|| '%';
RETURN QUERY
SELECT *
FROM products
WHERE name LIKE safe_pattern ESCAPE '!';
END;
$$ LANGUAGE plpgsql;
-- Usage
SELECT * FROM safe_like_search('50% off sale');
SELECT * FROM safe_like_search('price_drop');
Quick Fix Checklist
-- Always validate your ESCAPE clause before deploying
-- Rule: ESCAPE must be exactly 1 character
-- WRONG
... LIKE '%foo%' ESCAPE '!!'; -- two characters
... LIKE '%foo%' ESCAPE ''; -- empty string
-- RIGHT
... LIKE '%foo%' ESCAPE '!'; -- single character
... LIKE '%foo%' ESCAPE '\'; -- single backslash
... LIKE '%foo%'; -- no ESCAPE clause (uses default '\')
Prevention Tips
1. Create a centralized escape helper function
Never write raw LIKE escape logic inline. Standardize a single reusable function across your codebase to guarantee the escape character is always valid and consistent.
CREATE OR REPLACE FUNCTION escape_like(input_text TEXT)
RETURNS TEXT AS $$
BEGIN
RETURN replace(replace(replace(input_text, '!', '!!'), '%', '!%'), '_', '!_');
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT;
-- Consistent usage everywhere
SELECT * FROM products
WHERE name LIKE '%' || escape_like(user_input) || '%' ESCAPE '!';
2. Add SQL linting to your CI/CD pipeline
Use tools like pgsanity or sqlfluff to statically analyze SQL before deployment. Add a code review checklist item to verify that any ESCAPE clause contains exactly one character. Catching this at review time prevents runtime errors in production.
Related Errors
- 22025 — invalid_escape_sequence: The escape character itself is valid, but the escape sequence formed is not recognized.
-
22023 — invalid_parameter_value: Raised when
NULLor an otherwise invalid value is passed where a specific type is expected, often seen alongside pattern-matching errors.
📖 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)