PostgreSQL "index row size exceeds maximum" — Why Long Text Cannot Be Directly Indexed?
Root causes of PostgreSQL btree 1/3 page limit (2704 bytes), byte vs character calculations, expression indexes, MD5 hash indexing, and GIN trigram alternatives.
Operational Verification & Reliability Metadata
The error string is a symptom, not the root cause. Do not restart the server blindly. Run the diagnostic queries below to pinpoint the actual failure mode.
▸Symptoms
ERROR: index row size 3112 exceeds btree version 4 maximum 2704 for index "idx_makale_ozet"
DETAIL: Index row references tuple (0,14) in relation "makale".
HINT: Values larger than 1/3 of a buffer page cannot be indexed.This error surfaces during CREATE INDEX or subsequent INSERT/UPDATE operations. The root cause is architectural: in PostgreSQL B-Trees, an index tuple cannot exceed 1/3 of an 8 KB buffer page (~2,704 bytes net of page headers). This invariant guarantees that every internal B-Tree page accommodates at least three index tuples to preserve balance.
▸Diagnostic Query
Identify tuples exceeding the boundary:
SELECT id, length(ozet) AS char_length, octet_length(ozet) AS byte_length
FROM makale
ORDER BY octet_length(ozet) DESC
LIMIT 10;varchar(2000) column can expand to 4,000 bytes.▸Resolution Patterns
1. Exact Equality Lookup → MD5 / SHA-256 Hash Index
CREATE INDEX idx_makale_ozet_md5 ON makale (md5(ozet));
SELECT * FROM makale WHERE md5(ozet) = md5('query text...');2. Prefix Matching → Functional Expression Index
CREATE INDEX idx_makale_ozet_prefix ON makale (left(ozet, 200));
SELECT * FROM makale WHERE left(ozet, 200) = left('query text...', 200);3. Substring & ILIKE Searches → GIN Trigram
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE INDEX idx_makale_ozet_trgm ON makale USING gin (ozet gin_trgm_ops);4. Natural Language Search → tsvector
ALTER TABLE makale ADD COLUMN arama tsvector
GENERATED ALWAYS AS (to_tsvector('english', coalesce(baslik,'') || ' ' || coalesce(ozet,''))) STORED;
CREATE INDEX idx_makale_arama ON makale USING gin (arama);▸Index Key Limit Comparison: PostgreSQL vs MySQL
| Criteria | PostgreSQL (B-Tree) | MySQL InnoDB (DYNAMIC) | MySQL InnoDB (COMPACT) |
|---|---|---|---|
| Max Key Size | 2,704 bytes | 3,072 bytes | 767 bytes |
| utf8mb4 Chars | ~676 chars | 768 chars | 191 chars |
| Companion Guide | This Guide (H-21) | MySQL Error 1071 (H-22) | MySQL Error 1071 (H-22) |
- •Companion Guide: MySQL Error 1071 Specified key was too long
- •Engine Hub: PostgreSQL Production Hub
Other Database Error Triage Playbooks
Other battle-tested diagnostic guides for high-severity database errors.
PostgreSQL Connection Exhaustion & too many clients Triage
Root causes of FATAL: sorry, too many clients already and remaining connection slots errors, read-only pg_stat_activity diagnosis with privilege boundaries, and two-tier pooling mitigation.
PostgreSQL password authentication failed: Root Cause and Fix
Four root causes of psql password authentication failed: incorrect password, md5/SCRAM mismatch, pg_hba.conf rule ordering, and port/cluster confusion. Step-by-step triage.
PostgreSQL connection refused: 8-Step Definitive Triage
Difference between connection refused, timeout, and no pg_hba entry; triage for listen_addresses, port conflicts, firewalls, and Docker container mappings.