Now-Next

SaaS architecture

value too long for type character varying in PostgreSQL

Measured on PostgreSQL 12 to 18: why the varchar error names no column, how to find it, when values are cut silently, and which ALTER rewrites the table.

Chris van Eijk, Now-Next · · 6 min read

ERROR: value too long for type character varying(255) (SQLSTATE 22001) means a value has more characters than a varchar(n) column allows. The frustrating part is what the message leaves out: on PostgreSQL 12, 16, 17 and 18 it named only the type — no table, no column, no DETAIL, also in verbose mode, and psycopg2’s diag.table_name and diag.column_name were None. The server log is better: with the default log_min_error_statement = error it logged the full STATEMENT under the error, which names the table. In the message itself, with five varchar columns, the (n) is your only clue.

We measured the cases below on PostgreSQL 12.22, 16.15, 17.11 and 18.6, which gave the same messages except where noted, and the ALTER TABLE timings on one million rows.

How do you find which column is too long?

Start from the number in the message: character varying(100) means a column declared varchar(100). On a table with columns of 10, 50, 100, 255 and 20 characters and one 101-character value, the error said character varying(100). With two values too long, it named only one of them ((10)).

When several columns share a length, compare the values with the declared limits. Put the values of the failing insert into a VALUES list with the column names, and let jsonb_each_text turn them into rows:

with v(a, b, c, d, e) as (
  values ('kort', 'ook kort', repeat('x', 101), 'prima', 'goed')
)
select c.column_name, c.character_maximum_length as max_length,
       length(j.value) as value_length,
       length(j.value) > c.character_maximum_length as too_long
from v, jsonb_each_text(to_jsonb(v)) j
join information_schema.columns c
  on c.table_schema = 'public' and c.table_name = 't5' and c.column_name = j.key
order by c.ordinal_position;

It returned c | 100 | 101 | t and false for the other four. Quote mixed-case column names in v(…) exactly as in the table, or that row silently drops out of the join; columns without a limit and NULL values show NULL in too_long, and array columns (varchar(10)[]) are not covered.

COPY names the column, the value and the line (CONTEXT: COPY tc2, line 2, column city: "Leiden"), and PL/pgSQL on 16 and later names the variable (CONTEXT: PL/pgSQL assignment "v := '12345678901'"; on 12 only the line number). A multi-row insert gave the same bare message, without saying which row. When the driver binds parameters on the server, the logged statement only shows $1, $2, $3; on 13 and later, set log_parameter_max_length_on_error = -1 added the values, in the log and in the client’s CONTEXT (unnamed portal with parameters: $1 = '2', $2 = '12345678901', …). psycopg2 fills the values in on the client, so they were in the log already.

Does PostgreSQL count characters or bytes?

Characters — Unicode code points, not what a user sees: a decomposed é (e plus a combining accent) counts as two, and so does a flag emoji. In a UTF-8 database, ten é (20 bytes) and ten 😀 (40 bytes) both fitted in varchar(10); eleven 😀 did not. length() gives characters and octet_length() bytes — the limit is the first. Bytes that are not valid UTF-8 never get this far; they fail earlier with invalid byte sequence for encoding “UTF8”.

When is a too-long value cut silently instead?

In two cases, both measured and both in the documentation, which calls the first “this somewhat bizarre exception” and adds that each is “required by the SQL standard”:

What was storedResult
'1234567890 ' (10 characters + 3 spaces) into varchar(10) or char(10)no error; stored as 1234567890 — trailing spaces are dropped to fit
'123456789 x' or '12345678901 ' into varchar(10)22001: only spaces beyond the limit are dropped, anything else is an error
'…20 characters…'::varchar(10) or cast(… as varchar(10))no error; cut to 10 characters
insert into t select x::varchar(10) from …INSERT 0 1, stored cut to 10; without the cast, 22001
a prepare with a varchar(10) parameter22001 — a parameter type does not cut
array['12345678901']::varchar(10)[]no error; each element cut to 10 (without the cast, 22001)

The explicit cast is the one that loses data quietly: code that casts “to be safe” turns an error into a truncated name or address. If you would rather cut on purpose, left(value, 10) says so in the code.

What happened to a value longer than varchar(10)

How did it reach the column?

  • Inserted or updated as it isError 22001Unless only trailing spaces are beyond the limit: those are dropped.
  • Through ::varchar(10) or cast(…)Cut to 10, no errorThe rest of the value is gone.
  • Through COPYError 22001, with the column nameAnd the value and the line, in the CONTEXT line.
  • Through array[…]::varchar(10)[]Each element cut, no errorThe same rule as the scalar cast.

PostgreSQL 12.22, 16.15, 17.11 and 18.6.

Which ALTER TABLE rewrites the table?

Making a plain varchar(n) larger is cheap; anything else rewrites. On one million rows, comparing the table’s and index’s relfilenode before and after:

ChangeRewrite?Time
varchar(10) → varchar(255)no: same relfilenode for table and index0.9–1.1 ms
varchar(10) → varchar (no limit) or textno0.9–2.6 ms
a column of type domain kort as varchar(10) → varchar(255)yes: new relfilenode for table and index2.9–3.3 s
text → varchar(255), or varchar(255) → varchar(100), data fitsyes: new relfilenode for table and index2.1–2.6 s
→ varchar(5) while longer values existfails with 22001, nothing changed—
→ varchar(5) using left(name, 5)yes1.2–1.4 s

If a view, materialized view, row-level security policy or generated column uses the column, even widening is refused: 0A000: cannot alter type of a column used by a view or rule (with DETAIL: rule _RETURN on view v_tv depends on column "name"), or … used in a policy definition. Drop and recreate the dependent object in the same transaction. A view that does not select the column did not block it.

Even the cheap change took an ACCESS EXCLUSIVE lock, on 12, 16, 17 and 18, so on a busy shared table it waits behind running queries and blocks new ones while it waits — set a lock_timeout, as measured in schema migrations on a shared multi-tenant table.

Should you use varchar(n) or text with a check?

If the limit is a business rule, a text column with a CHECK gives a better error: 23514: new row for relation "tt" violates check constraint "tt_name_check", with the whole row in DETAIL and TABLE NAME and CONSTRAINT NAME filled in — and the generated constraint name contains the column. A domain (create domain korte_naam as text check (length(value) <= 10)) gave value for domain korte_naam violates check constraint "korte_naam_check" — again without table or column. In psycopg2 both are CheckViolation, an IntegrityError rather than a DataError, so an except DataError no longer catches them. Changing the limit is then dropping and adding a constraint instead of changing a type: no rewrite, a scan of 77 ms on one million rows under an exclusive lock — or add it NOT VALID (0.2 ms) and VALIDATE it afterwards (77 ms) under a weaker lock. The one difference to know: a check counted trailing spaces, so '1234567890 ' that varchar(10) accepted was refused.

What we did not measure

JDBC and other drivers, apart from psycopg2 (psycopg2.errors.StringDataRightTruncation, a subclass of DataError). Databases in encodings other than UTF-8. Where the common 255 limit comes from in specific ORMs.

Sources: PostgreSQL 17 documentation on character types, read on 22 October 2026. All measurements are our own, on PostgreSQL 12.22, 16.15, 17.11 and 18.6 in containers, with psycopg2 2.9.13, on 22 October 2026.

Let's talk

What are we building?

One conversation is enough to know whether we fit. Tell us what you have in mind — we will tell you how fast it can happen, and whether we are the right people for it.

Start the conversation