How to fix Invalid byte sequence for encoding UTF8 in PostgreSQL
This error occurs when PostgreSQL encounters data that doesn't conform to UTF-8 encoding. Common causes include importing data from non-UTF8 sources, null bytes in text fields, or client/server encoding mismatches. Ensure data is properly encoded or use appropriate binary formats.
Quick fix
Fixes 22021
-- Connect to PostgreSQL
psql -U username -d databasename
-- Check database encoding
\l databasename
-- Or query directly
SELECT datname, pg_encoding_to_char(encoding)
FROM pg_database
WHERE datname = 'databasename';- Check the database encoding
- Identify the problematic row and byte sequence
- Convert the data file to UTF-8
- Remove null bytes from data
-- Connect to PostgreSQLpsql -U username -d databasename-- Check database encoding\l databasename-- Or query directlySELECT datname, pg_encoding_to_char(encoding)FROM pg_databaseWHERE datname = 'databasename';Invalid byte sequence for encoding UTF8
What this error means
PostgreSQL is configured to use UTF-8 encoding but received byte sequences that are invalid in UTF-8. UTF-8 is a variable-length character encoding where characters are represented by sequences of bytes. Invalid sequences occur when byte patterns don't match valid UTF-8 structure. This often happens during data import (COPY, INSERT), when loading data from different encoding sources, or when handling special characters like null bytes that UTF-8 cannot represent in text fields. PostgreSQL strictly validates encoding to prevent data corruption and ensure consistent behavior across systems.
Typical symptoms
- COPY command fails with 'invalid byte sequence for encoding UTF8' error
- INSERT statements fail when loading data from CSV or other files
- Data import stops at specific rows containing problematic characters
- Error message shows hex value like '0x00', '0xfc', or other byte sequences
- Application import tools (pg_dump restore, COPY, bulk loaders) abort
- Works fine locally but fails when deploying to production database
- Exporting data from Oracle, MySQL, or other databases fails on import
Common causes
- Importing data encoded in a different charset (Latin-1, Windows-1252, etc.) without converting to UTF-8 first
- CSV or data files not properly saved in UTF-8 encoding by the source application
- Null bytes (0x00) in data—PostgreSQL text fields cannot store null characters, only bytea fields can
- Corrupted data files with mixed encodings or incomplete multi-byte UTF-8 sequences
- Client encoding mismatch—server expects UTF-8 but client sends different encoding without conversion
- Binary data passed directly to text columns instead of bytea columns
- pg_dump restore from database with different encoding (e.g., SQL_ASCII or Latin-1 source)
How to fix "Invalid byte sequence for encoding UTF8"
Verify your database is configured for UTF-8:
-- Connect to PostgreSQL
psql -U username -d databasename
-- Check database encoding
\l databasename
-- Or query directly
SELECT datname, pg_encoding_to_char(encoding)
FROM pg_database
WHERE datname = 'databasename';If the database is not UTF-8, you'll need to recreate it with UTF-8 encoding (cannot be changed on existing database). Otherwise, note the current encoding and ensure your data source matches.
The error message usually includes context showing which row caused the issue:
ERROR: invalid byte sequence for encoding UTF8: 0xfc CONTEXT: COPY mytable, line 42This tells you the problem is at line 42 of your data file. Check that specific line for:
- Special or binary characters
- Null bytes (0x00)
- Characters from a different encoding (like 0xfc from Latin-1 'ü')
You can inspect a file's encoding with:
# Check file encoding
file -i yourfile.csv
# Show hex dump of problematic area
xdump -C yourfile.csv | head -50If the source file is in a different encoding, convert it to UTF-8 using iconv:
# Detect source encoding (e.g., Latin-1, Windows-1252, ISO-8859-1)
file -i yourfile.csv
# Convert to UTF-8
iconv -f ISO-8859-1 -t UTF-8 yourfile.csv > yourfile_utf8.csv
# Or for Windows-1252
iconv -f WINDOWS-1252 -t UTF-8 yourfile.csv > yourfile_utf8.csv
# Verify the conversion
file -i yourfile_utf8.csvThen use the converted file for import.
If the error shows 0x00 (null byte), the data contains null characters which UTF-8 text fields cannot store. Remove them:
# Remove null bytes from file
tr -d '\0' < yourfile.csv > yourfile_clean.csvOr in the COPY command, you can use the FORMAT to handle binary data:
COPY mytable FROM STDIN WITH (FORMAT BINARY);However, if you're loading text data with null bytes, consider:
- Storing the data in bytea (binary) column instead of text
- Replacing null bytes with a placeholder (space, dash, etc.)
- Filtering them out during preprocessing
Ensure the client encoding matches your data when using COPY:
-- Connect to database
psql -U username -d databasename
-- Check current client encoding
\encoding
-- Change client encoding if needed
\encoding UTF8
-- Or set environment variable before connecting
export PGCLIENTENCODING=UTF8
psql -U username -d databasename
-- Then run COPY
COPY mytable FROM '/path/to/file.csv' WITH (FORMAT csv);You can also specify encoding in the COPY command directly:
COPY mytable FROM STDIN WITH (FORMAT csv, ENCODING 'UTF8');For large imports, use OPTIONS to skip problematic rows:
-- PostgreSQL 14+: Continue on error
COPY mytable FROM '/path/to/file.csv'
WITH (FORMAT csv, ON_ERROR CONTINUE);
-- For older versions, use a wrapper script
-- or preprocess the file to remove bad rowsThis allows partial imports where only valid UTF-8 rows are loaded.
Alternatively, preprocess the file to sanitize encoding:
# Remove or replace non-UTF8 sequences
perldoc -T perlunifaq
# Or use:
python3 -c "import sys; print(open('file.csv', encoding='utf-8', errors='ignore').read(), end='')" > file_clean.csvIf you need to store binary data (like null bytes), use bytea columns instead of text:
-- Define column as bytea, not text
CREATE TABLE mytable (
id SERIAL PRIMARY KEY,
data bytea
);
-- Or alter existing column
ALTER TABLE mytable ALTER COLUMN data TYPE bytea;
-- Insert binary data
INSERT INTO mytable (data) VALUES (E'\\x48656c6c6f'::bytea);
-- Or from a file
COPY mytable (data) FROM STDIN WITH (FORMAT BINARY);bytea fields use binary format that preserves all byte sequences without encoding validation.
After fixing the data, test with a small subset before importing everything:
# Extract first 100 lines
head -100 yourfile_utf8.csv > sample.csv
# Try importing the sample
psql -U username -d databasename -c "COPY mytable FROM STDIN WITH (FORMAT csv)" < sample.csvIf the sample imports successfully, proceed with the full file:
psql -U username -d databasename -c "COPY mytable FROM STDIN WITH (FORMAT csv)" < yourfile_utf8.csvFor large files, consider using a staging table first:
CREATE TEMP TABLE staging LIKE mytable;
COPY staging FROM STDIN WITH (FORMAT csv);
INSERT INTO mytable SELECT * FROM staging WHERE is_valid();Advanced notes
Encoding Validation: PostgreSQL strictly enforces UTF-8 at the byte level. Invalid sequences can come from incomplete multi-byte sequences (e.g., lone continuation bytes) or out-of-order bytes. The specific hex value in the error (0xfc, 0x00) tells you exactly which byte violated UTF-8 rules. Null Byte Limitation: PostgreSQL text fields cannot store null bytes because historically PostgreSQL used C-style null-terminated strings internally. This limitation applies to VARCHAR, TEXT, and CHAR types. If you need null bytes, use bytea. Binary COPY Format: The FORMAT BINARY option in COPY tells PostgreSQL the data is already in the server's native binary format, bypassing all encoding validation. This is fast but requires the data to be in the exact format PostgreSQL expects. SQL_ASCII Compatibility: Databases created with SQL_ASCII encoding are special—they don't validate encoding at all, treating each byte independently. If migrating from SQL_ASCII, bytes 128-255 may be invalid UTF-8. Use iconv or encoding detection tools. pg_dump Encoding: When using pg_dump to backup a non-UTF8 database, the dump file will be in that encoding. Restore it to a UTF8 database with: PGCLIENTENCODING=original_encoding pg_restore -d newdb dump.custom. Performance Note: Encoding validation has minimal performance impact. Most slowness comes from I/O and parsing, not character set checking.
Frequently asked questions
- What does "22021" mean?
- PostgreSQL is configured to use UTF-8 encoding but received byte sequences that are invalid in UTF-8. UTF-8 is a variable-length character encoding where characters are represented by sequences of bytes. Invalid sequences occur when byte patterns don't match valid UTF-8 structure.
- How do I fix "22021"?
- This error occurs when PostgreSQL encounters data that doesn't conform to UTF-8 encoding. Common causes include importing data from non-UTF8 sources, null bytes in text fields, or client/server encoding mismatches. Ensure data is properly encoded or use appropriate binary formats.
- What causes "22021"?
- Importing data encoded in a different charset (Latin-1, Windows-1252, etc.) without converting to UTF-8 first CSV or data files not properly saved in UTF-8 encoding by the source application Null bytes (0x00) in data—PostgreSQL text fields cannot store null characters, only bytea fields can
- Is the "22021" error serious?
- Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.
Official resources & further reading
- 📚PostgreSQL Official Documentation
- 📖postgresql.org - errcodes appendix(Official Docs)