When an identifier — such as a table name, column name, index name, or constraint name — exceeds PostgreSQL's maximum allowed length, you will see an error like:
ERROR: identifier "my_very_long_table_name_that_exceeds_the_maximum_allowed_length_in_postgres" will be truncated to "my_very_long_table_name_that_exceeds_the_maximum_allowed_lengt"
or in some contexts:
ERROR: name "my_very_long_index_name_that_is_clearly_too_long_for_postgresql" is too long
This is SQLSTATE 42622, condition name name_too_long, and it falls under error class 42 (Syntax Error or Access Rule Violation).
What This Error Means
PostgreSQL uses an internal type called Name for storing identifiers. The maximum length of this type is controlled by the compile-time constant NAMEDATALEN, which defaults to 64 bytes. Identifiers are stored as null-terminated strings in this fixed-size field, so the effective maximum length for any identifier is 63 bytes (NAMEDATALEN - 1).
This limit applies to all identifier types: table names, column names, index names, constraint names, sequence names, schema names, function names, role names, and so on. Importantly, the limit is in bytes, not characters. For identifiers using only ASCII characters this makes no difference, but multi-byte UTF-8 characters (such as accented letters or CJK characters) count as multiple bytes and will hit the limit sooner.
The behavior depends on context. In many DDL statements, PostgreSQL silently truncates the identifier and emits a WARNING rather than a hard ERROR. In other contexts — particularly when matching identifiers specified by the user — the truncation can cause subtle bugs where the name you intended does not match the name actually stored. Applications relying on auto-generated long names (such as ORM-generated constraint names) are especially susceptible.
Common Causes
Auto-generated names from ORMs or migration tools. Frameworks like Django, SQLAlchemy, Rails (ActiveRecord), and Hibernate generate constraint, index, and foreign key names by concatenating table and column names. These generated names frequently exceed 63 bytes for tables with longer names.
Manually specified long identifiers in DDL. Writing a
CREATE TABLE,CREATE INDEX, orALTER TABLEstatement with an identifier longer than 63 bytes — often from copying names from another system that has a higher limit.Schema migrations that compound names. Renaming tables or columns during migrations without updating dependent index and constraint names can produce new compound names that are too long.
Multi-byte character identifiers. Using Unicode identifiers where each character may be 2–4 bytes. A 20-character identifier using 4-byte characters already exceeds the 63-byte limit.
Programmatic SQL generation. Application code that builds identifier names dynamically from user input or configuration values without checking length.
How to Fix name_too_long
Shorten the identifier. The simplest fix is to rename the object to something under 63 bytes. For existing objects, use
ALTER:-- Rename a too-long index ALTER INDEX my_very_long_index_name_that_exceeds_postgresql_limit RENAME TO shorter_index_name; -- Rename a too-long constraint ALTER TABLE orders RENAME CONSTRAINT orders_customer_id_product_id_quantity_fkey TO orders_customer_product_fk;Fix ORM-generated names explicitly. Rather than letting your ORM auto-generate names, specify them explicitly and keep them short:
# Django example — specify the constraint name explicitly class Meta: constraints = [ models.UniqueConstraint( fields=['customer_id', 'product_id'], name='orders_cust_prod_uniq' # short, explicit name ) ]Check identifier length before executing DDL. Add a check in your migration scripts:
-- Check all current identifiers approaching the limit SELECT relname, length(relname) AS len FROM pg_class WHERE length(relname) >= 60 ORDER BY len DESC; -- Check constraint names SELECT conname, length(conname) AS len FROM pg_constraint WHERE length(conname) >= 60 ORDER BY len DESC;Handle truncation warnings in application code. If you are programmatically generating names, truncate and deduplicate them yourself before passing to PostgreSQL:
def safe_identifier(name: str, max_bytes: int = 63) -> str: encoded = name.encode('utf-8') if len(encoded) <= max_bytes: return name # Truncate to max_bytes, avoiding splitting a multi-byte char truncated = encoded[:max_bytes].decode('utf-8', errors='ignore') return truncatedReview and catch WARNING-level truncations. PostgreSQL may silently truncate and continue with a warning rather than raising a hard error. Ensure your application or migration framework treats these warnings as errors, or review
pg_indexesandpg_constraintafter migrations to confirm names match what you intended.
Additional Information
- The
NAMEDATALENconstant is set at compile time and is64in all standard PostgreSQL distributions. It is theoretically possible to recompile PostgreSQL with a higher value, but this is strongly discouraged because it changes the on-disk format and breaks binary compatibility. - SQLSTATE
42622is in error class42(Syntax Error or Access Rule Violation), alongside conditions like42601(syntax_error),42703(undefined_column), and42P01(undefined_table). - The truncation behavior (WARNING vs. ERROR) has been consistent across all modern PostgreSQL versions. The server will warn rather than fail in DDL contexts, which means silent data divergence is possible if warnings are ignored.
- JDBC drivers surface this as a
PSQLExceptionwith SQLState42622. Most other drivers (psycopg2, asyncpg, node-postgres) expose the SQLState directly on the exception object. - Cloud-managed PostgreSQL services (RDS, Cloud SQL, Azure Database for PostgreSQL) all enforce the same 63-byte limit since it is a compiled-in constant, not a configuration parameter.
Frequently Asked Questions
Can I increase the 63-byte identifier limit in PostgreSQL?
Not in a standard installation. NAMEDATALEN is a compile-time constant hardcoded to 64 (63 usable bytes). You would need to recompile PostgreSQL from source to change it, which changes the on-disk storage format and makes your installation incompatible with standard tools and binary upgrades. This is not recommended for production systems.
Why does PostgreSQL sometimes warn instead of error?
In DDL contexts (e.g., CREATE TABLE, CREATE INDEX), PostgreSQL truncates the identifier and emits a WARNING: identifier "..." will be truncated to "..." rather than failing. The operation succeeds with the truncated name. This allows existing applications to function, but it means the name stored differs from the name you specified, which can cause subtle lookup failures later.
How do I find all identifiers in my database that are close to or at the limit? Query the catalog tables directly:
SELECT 'table' AS type, relname AS name, length(relname) AS byte_len
FROM pg_class WHERE length(relname) >= 60
UNION ALL
SELECT 'constraint', conname, length(conname)
FROM pg_constraint WHERE length(conname) >= 60
UNION ALL
SELECT 'index', indexname, length(indexname)
FROM pg_indexes WHERE length(indexname) >= 60
ORDER BY byte_len DESC;
Does this limit apply to schema-qualified names like myschema.mytable?
No — the 63-byte limit applies to each component of a qualified name individually, not to the full dotted path. myschema.mytable is checked as myschema (9 bytes) and mytable (7 bytes) separately, each well within the limit. Only the individual identifier segments are subject to NAMEDATALEN.