MySQL ERROR 1071 occurs when you create an index that exceeds the maximum allowed key length. This commonly happens with UTF-8 or UTF-8MB4 character sets when indexing VARCHAR columns, or when combining multiple columns in a composite index. The fix depends on your MySQL version and storage engine configuration.
MySQL ERROR 1071 (Specified key was too long) is a structural error that occurs when you attempt to create an index with a combined byte length that exceeds MySQL's maximum allowed index size. Unlike authentication or connection errors, this prevents table creation or modification entirely. The error manifests differently depending on context: - **InnoDB with utf8mb4 charset**: Maximum 767 bytes per index key (MySQL 5.6) or 3072 bytes (MySQL 5.7+) - **MyISAM storage engine**: Maximum 1000 bytes per index key - **InnoDB with utf8 charset**: Approximately 255 characters (3 bytes per character) = 765 bytes max - **InnoDB with utf8mb4 charset**: Approximately 191 characters (4 bytes per character) = 764 bytes max The key insight: Character sets affect byte length. UTF-8 uses 3 bytes per character, UTF-8MB4 uses up to 4 bytes per character, while latin1 uses only 1 byte per character. When you define VARCHAR(255) with utf8mb4, MySQL reserves 255 × 4 = 1020 bytes for the index, exceeding the 767-byte limit.
First, understand your MySQL version and existing table configuration, as solutions differ significantly.
Check MySQL version:
-- See full version information
SELECT VERSION();
-- See if large index support is built-in
SHOW VARIABLES LIKE 'innodb_large_prefix';Check table row format (if table already exists):
-- View table information
SHOW CREATE TABLE your_table_name\G
-- Or check row format directly
SELECT TABLE_NAME, ENGINE, ROW_FORMAT FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'your_table_name';Key version differences:
- MySQL 5.6 and earlier: Maximum 767 bytes for InnoDB, 1000 bytes for MyISAM
- MySQL 5.7: Supports 3072 bytes if innodb_large_prefix=ON (must be enabled in config)
- MySQL 8.0+: Supports 3072 bytes by default, innodb_large_prefix parameter removed
Row format differences:
- COMPACT/REDUNDANT: Maximum 767 bytes per key (legacy formats)
- DYNAMIC/COMPRESSED: Maximum 3072 bytes per key (requires MySQL 5.7.7+)
The quickest fix for most situations is to use prefix indexing, which indexes only the first N characters of a column instead of the entire value.
Calculate safe prefix length for your charset:
- latin1 charset: 767 characters (1 byte each)
- utf8 charset: 255 characters (3 bytes each = 765 bytes)
- utf8mb4 charset: 191 characters (4 bytes each = 764 bytes)
Apply prefix indexing in CREATE TABLE:
-- Original failing table (with utf8mb4)
-- CREATE TABLE users (
-- id INT PRIMARY KEY,
-- email VARCHAR(255) UNIQUE, -- 255 × 4 = 1020 bytes, exceeds 767 limit
-- name VARCHAR(255),
-- UNIQUE KEY idx_email (email) -- FAILS
-- );
-- Fixed version using prefix indexes
CREATE TABLE users (
id INT PRIMARY KEY,
email VARCHAR(255),
name VARCHAR(255),
UNIQUE KEY idx_email (email(191)) -- Index only first 191 chars
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- For composite indexes (multiple columns)
CREATE TABLE posts (
id INT PRIMARY KEY,
title VARCHAR(255),
slug VARCHAR(255),
category VARCHAR(100),
UNIQUE KEY idx_slug (slug(100)), -- First 100 chars of slug
INDEX idx_title_cat (title(75), category(50)) -- Composite index with prefixes
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Or create indexes separately
CREATE UNIQUE INDEX idx_email ON users (email(191));
CREATE INDEX idx_title_category ON posts (title(75), category(50));Verify the prefix length works:
-- After creating table successfully, check the index
SHOW CREATE TABLE users\G
-- See actual index information
SELECT * FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_NAME = 'users' AND COLUMN_NAME = 'email';Trade-offs with prefix indexing:
- Pro: Works on MySQL 5.6 without configuration changes
- Pro: No need to alter column sizes
- Con: Index only guarantees uniqueness of first N characters (data duplication not detected)
- Con: May need longer prefixes for highly variable data (email addresses are good candidates for prefix indexes)
Use prefix indexes when:
- Indexing VARCHAR(255+) columns with utf8mb4
- You have many unique values in the first N characters
- Email, URLs, or other naturally-unique-at-prefix data
- Working on MySQL 5.6 and can't upgrade
If you're on MySQL 5.7 or later, the cleanest solution is to change the row format to DYNAMIC, which supports 3072-byte indexes without configuration changes.
Change row format for new table:
-- Create table with DYNAMIC row format
CREATE TABLE users (
id INT PRIMARY KEY,
email VARCHAR(255) UNIQUE,
name VARCHAR(255),
UNIQUE KEY idx_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;
-- Create another table
CREATE TABLE posts (
id INT PRIMARY KEY,
title VARCHAR(255),
content TEXT,
slug VARCHAR(255) UNIQUE,
UNIQUE KEY idx_slug (slug)
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC DEFAULT CHARSET=utf8mb4;If the table already exists, alter it:
-- Change existing table row format
ALTER TABLE users ROW_FORMAT=DYNAMIC;
-- This may take time on large tables
-- Check if it completed
SHOW CREATE TABLE users\GVerify the change:
-- Check row format was applied
SELECT TABLE_NAME, ROW_FORMAT FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'users';Requirements for DYNAMIC row format:
- MySQL 5.7.7 or later (5.7.6 and earlier: DYNAMIC is not available)
- InnoDB storage engine
- innodb_file_format should be set to Barracuda (usually default in 5.7+)
- innodb_file_per_table should be ON (usually default)
Why DYNAMIC works:
- COMPACT/REDUNDANT: Store first 768 bytes of variable-length columns inline, rest in overflow pages
- DYNAMIC/COMPRESSED: All variable-length column data stored in overflow pages
- This removes the 767-byte in-line storage limit, allowing up to 3072 bytes in indexes
Trade-offs:
- Pro: No code changes needed, no prefix length calculations
- Pro: Supports full VARCHAR length in indexes
- Con: Requires MySQL 5.7.7+ (not available on 5.6)
- Con: Slight performance difference (usually negligible)
If you're on MySQL 5.6 or 5.7 and can't change row format, enable innodb_large_prefix in your MySQL configuration. This requires accessing the my.cnf or my.ini file.
Edit MySQL configuration file:
# Linux - find and edit my.cnf
sudo nano /etc/mysql/my.cnf
# or
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
# macOS (Homebrew)
nano /usr/local/etc/my.cnf
# Windows - edit my.ini
# Typically in: C:\ProgramData\MySQL\MySQL Server 5.7\my.iniAdd these lines under [mysqld] section:
[mysqld]
innodb_file_format=barracuda
innodb_file_per_table=on
innodb_large_prefix=onFull example configuration:
[mysqld]
# Performance settings
max_connections=100
default-storage-engine=InnoDB
# Large index support
innodb_file_format=barracuda # Enable Barracuda file format
innodb_file_per_table=on # Create separate tablespace per table
innodb_large_prefix=on # Enable large prefix indexes (3072 bytes)
# Character set
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ciAfter editing, restart MySQL:
# Linux/systemd
sudo systemctl restart mysql
# macOS (Homebrew)
brew services restart mysql
# Windows (Command Prompt as Administrator)
net stop MySQL57
net start MySQL57
# Or direct startup
mysqld --consoleVerify settings applied:
-- Check if large prefix is enabled
SHOW VARIABLES LIKE 'innodb_large_prefix';
-- Check file format
SHOW VARIABLES LIKE 'innodb_file_format';
-- Check file per table
SHOW VARIABLES LIKE 'innodb_file_per_table';Now create tables with ROW_FORMAT=DYNAMIC:
CREATE TABLE users (
id INT PRIMARY KEY,
email VARCHAR(255) UNIQUE,
name VARCHAR(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;Limitations of innodb_large_prefix:
- Only works with DYNAMIC or COMPRESSED row formats (set ROW_FORMAT in CREATE TABLE)
- Does not work with COMPACT row format (default in older versions)
- MySQL 8.0 removed innodb_large_prefix - automatically supports 3072 bytes
- Configuration applies only to new tables - existing COMPACT tables still limited to 767 bytes
When to use innodb_large_prefix:
- Stuck on MySQL 5.6 or 5.7 that you can't upgrade
- Need to avoid code changes (prefix indexing)
- Have server configuration access
- Can tolerate minor row storage differences
If you don't need the full 255-character capacity, reducing VARCHAR size is a valid solution that works on all MySQL versions.
Analyze current column usage:
-- See actual max length used in email column
SELECT MAX(CHAR_LENGTH(email)) as max_email_length FROM users;
-- For other columns
SELECT MAX(CHAR_LENGTH(name)) as max_name_length FROM users;
SELECT MAX(CHAR_LENGTH(category)) as max_category_length FROM products;Adjust column sizes based on usage:
-- If emails are typically 50-100 chars, reduce from 255
ALTER TABLE users MODIFY email VARCHAR(100) CHARSET utf8mb4;
-- Calculate byte size: 100 × 4 = 400 bytes (under 767 limit)
-- For composite indexes, reduce both columns
ALTER TABLE posts
MODIFY title VARCHAR(150) CHARSET utf8mb4,
MODIFY slug VARCHAR(100) CHARSET utf8mb4;
-- Byte calculation: (150 × 4) + (100 × 4) = 1000 bytes
-- Still over 767 limit! Need to reduce further or use prefix indexesSafe column sizes for utf8mb4 with 767-byte limit:
- Single column index: VARCHAR(191) = 191 × 4 = 764 bytes
- Two-column index: VARCHAR(95) + VARCHAR(95) = (95+95) × 4 = 760 bytes
- Three-column index: VARCHAR(63) + VARCHAR(63) + VARCHAR(63) = 189 × 4 = 756 bytes
Example real-world reduction:
-- Before (fails with error 1071)
CREATE TABLE ecommerce_products (
id INT PRIMARY KEY,
sku VARCHAR(255) UNIQUE, -- Max SKU length in practice: 50 chars
category VARCHAR(255), -- Max category: 80 chars
description VARCHAR(255), -- Not indexed
UNIQUE KEY idx_sku (sku), -- sku: 255 × 4 = 1020 bytes - FAILS
KEY idx_category (category) -- category: 255 × 4 = 1020 bytes - FAILS
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- After (uses actual data requirements)
CREATE TABLE ecommerce_products (
id INT PRIMARY KEY,
sku VARCHAR(50) UNIQUE, -- 50 × 4 = 200 bytes
category VARCHAR(80), -- 80 × 4 = 320 bytes
description VARCHAR(255), -- Not indexed, so size doesn't matter
UNIQUE KEY idx_sku (sku), -- sku: 200 bytes - OK
KEY idx_category (category) -- category: 320 bytes - OK
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;Trade-offs with column reduction:
- Pro: Works on all MySQL versions (5.5, 5.6, 5.7, 8.0)
- Pro: No configuration changes needed
- Pro: May improve performance (smaller indexes, less disk space)
- Con: Limits actual data you can store
- Con: Breaking change if you have data that won't fit
- Con: Application validation must enforce the limit too
As a last resort, you could change to latin1 charset (1 byte per character), which allows 767-character columns. However, this is not recommended for most applications.
Change charset for new table:
-- Create with latin1 (no UTF-8 support)
CREATE TABLE users (
id INT PRIMARY KEY,
email VARCHAR(255) UNIQUE,
name VARCHAR(255),
UNIQUE KEY idx_email (email) -- 255 × 1 = 255 bytes - works fine
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci;Change charset for existing table:
-- This converts all text columns
ALTER TABLE users CONVERT TO CHARACTER SET latin1 COLLATE latin1_swedish_ci;
-- Or for specific columns
ALTER TABLE users MODIFY email VARCHAR(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci;Why NOT to use latin1:
- Loses Unicode support: Cannot store emoji, special international characters
- Data corruption risk: Cannot store non-latin1 characters (Chinese, Arabic, Cyrillic, etc.)
- One-way migration: Converting UTF-8 data to latin1 loses information permanently
- Compatibility issues: Modern applications expect UTF-8
- API problems: JSON APIs, email headers, web standards all expect UTF-8
Only use latin1 if:
- Your data is guaranteed ASCII-only (no special characters)
- You control all client applications
- You never need international character support
- You're maintaining legacy systems
Modern applications should always use utf8mb4, not latin1.
After applying your chosen fix, verify that table creation succeeds and indexes are properly created.
Test table creation with full indexes:
# If using a migration tool
npm run migrate
# or
python manage.py migrate
# or
php artisan migrate
# Or run your SQL directly
mysql -u username -p database_name < schema.sqlCheck table was created successfully:
-- View table structure
SHOW CREATE TABLE your_table_name\G
-- Check all indexes on the table
SHOW INDEX FROM your_table_name;
-- View detailed index information
SELECT
TABLE_NAME,
INDEX_NAME,
COLUMN_NAME,
SEQ_IN_INDEX,
KEY_LEN,
NULL_FLAG,
INDEX_TYPE
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_NAME = 'your_table_name'
ORDER BY INDEX_NAME, SEQ_IN_INDEX;Verify index byte length:
-- KEY_LEN shows bytes used by index
-- For email(191) with utf8mb4: should show 764 or less
-- For email(255) with utf8mb4 and DYNAMIC: should show 1020
-- Example output interpretation:
-- KEY_LEN = 764 means 191 characters × 4 bytes
-- KEY_LEN = 1020 means 255 characters × 4 bytes
-- KEY_LEN = 767 means 255 characters × 3 bytes utf8 (not utf8mb4)Test that indexes work correctly:
-- Insert test data
INSERT INTO users (id, email, name) VALUES (1, '[email protected]', 'Test User');
-- Verify unique constraint works
INSERT INTO users (id, email, name) VALUES (2, '[email protected]', 'Another User');
-- Should fail: Duplicate entry for key 'idx_email'
-- Check query uses the index
EXPLAIN SELECT * FROM users WHERE email = '[email protected]';
-- Should show index in "possible_keys" and "key" columnsPerformance check after migration:
-- Check table statistics
SHOW TABLE STATUS LIKE 'users'\G
-- Check row count
SELECT COUNT(*) FROM users;
-- Test query performance
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE email = '[email protected]'\GIf still getting error 1071:
-- Double-check your fix was applied
SHOW CREATE TABLE your_table_name\G
-- Verify MySQL variables
SHOW VARIABLES LIKE 'innodb_%';
SHOW VARIABLES LIKE 'character_set%';
-- Check server version
SELECT VERSION();MySQL Index Size Architecture:
The 767-byte limit originated from InnoDB's COMPACT and REDUNDANT row formats, which stored the first 768 bytes of variable-length column data inline within the page, with overflow to external pages. This meant index keys could be at most 767 bytes.
MySQL 5.7 introduced Barracuda file format with DYNAMIC and COMPRESSED row formats, which store variable-length data entirely in overflow pages, allowing index keys up to 3072 bytes. MySQL 8.0 made this the default behavior without requiring explicit innodb_large_prefix configuration.
Character Set Byte Calculations:
When MySQL calculates index key length, it uses the maximum number of bytes per character for the charset, not the actual data:
- latin1: 1 byte per character
- utf8: 3 bytes per character (MySQL's utf8 is actually UTF-8 with 3-byte limit)
- utf8mb4: 4 bytes per character (true UTF-8, supports 4-byte characters like emoji)
For VARCHAR(255) with utf8mb4:
- Declared size: 255 characters
- Byte reservation in index: 255 × 4 = 1020 bytes
- Result: Exceeds 767-byte limit on MySQL 5.6
Prefix Index Behavior:
When you use prefix indexes like KEY idx_email (email(191)):
- MySQL stores only the specified number of characters in the index
- Uniqueness is guaranteed only for those characters
- Full column value must be checked when retrieving data
- Suitable for VARCHAR columns where first N characters are naturally unique
Storage engine differences:
- MyISAM: Simple fixed 1000-byte limit
- InnoDB COMPACT/REDUNDANT: 767-byte limit due to inline storage
- InnoDB DYNAMIC/COMPRESSED: 3072-byte limit (no inline storage limit)
Version Timeline:
MySQL 5.5 and earlier:
- Only 767-byte limit for all indexes
- No Barracuda support
MySQL 5.6:
- Introduced Barracuda format with innodb_large_prefix
- Still defaults to COMPACT row format
- innodb_large_prefix OFF by default
- Requires manual configuration to support 3072-byte indexes
MySQL 5.7:
- Barracuda became default file format (version 5.7.7+)
- innodb_large_prefix ON by default
- DYNAMIC row format widely available
- Easier to use 3072-byte indexes
MySQL 8.0:
- Removed innodb_file_format and innodb_large_prefix parameters
- All new tables use DYNAMIC row format by default
- Automatically supports 3072-byte indexes
- Simplified configuration
MariaDB:
- Similar support to corresponding MySQL versions
- May have different defaults (check documentation for your version)
Practical Considerations:
For new projects:
- Always use MySQL 5.7+ or 8.0+
- Use utf8mb4 for full Unicode support
- Use DYNAMIC row format explicitly for clarity
- Don't use prefix indexes unless necessary
For existing projects:
- Assess if you can upgrade MySQL version
- Check if innodb_large_prefix is available
- Use prefix indexes as interim solution
- Plan migration to newer MySQL version
Performance Impact:
Prefix indexes:
- Slightly faster key comparisons (shorter keys)
- Need full column lookup for uniqueness verification
- Usually negligible difference in practice
DYNAMIC vs COMPACT:
- DYNAMIC uses slightly more space (overflow pages)
- Performance difference usually unmeasurable
- DYNAMIC is standard practice in modern MySQL
Index organization:
- Use covering indexes to avoid row lookups
- Keep indexes narrow when possible
- Monitor index usage with INFORMATION_SCHEMA.STATISTICS
Debugging Index Size Issues:
If you still get error 1071 after applying fixes:
1. Check SHOW CREATE TABLE - verify ROW_FORMAT was applied
2. Verify all columns in composite index byte lengths
3. Account for nullable columns (extra byte for NULL flag)
4. Check if migration tool lowercases or changes settings
5. Test directly with mysql CLI to isolate tool-specific issues
Migration Tool Considerations:
Doctrine (PHP):
- Supports @ORM\Index(name="idx_name", columns={"column(191)"})
- Defaults to 255 VARCHAR unless specified
- Set platform option for utf8mb4: "charset" => "utf8mb4"
Sequelize (Node.js):
- Use indexes: { fields: ["email"], unique: true }
- For prefix: indexes: { fields: [Sequelize.literal('email(191)')]
- Set charset in model: charset: 'utf8mb4'
Laravel:
- Defaults to 191 character prefix for string() columns in MySQL
- Can override: $table->string('name', 100)->index()
- Use fullText() or unsignedBigInteger for special cases
SQLAlchemy (Python):
- Set pool_pre_ping=True for connection issues
- Use String(length) with mysql_length parameter
- Configure charset in create_engine()
Most modern frameworks handle this automatically, but verify your version and config.
ERROR 1064: You have an error in your SQL syntax
How to fix "ERROR 1064: You have an error in your SQL syntax" in MySQL
ERROR 1054: Unknown column in field list
Unknown column in field list
ER_WINDOW_RANGE_FRAME_NUMERIC_TYPE (3589): RANGE frame requires numeric ORDER BY expression
RANGE frame requires numeric ORDER BY expression in MySQL window functions
CR_ALREADY_CONNECTED (2058): Handle already connected
How to fix "CR_ALREADY_CONNECTED (2058): Handle already connected" in MySQL
ER_WINDOW_DUPLICATE_NAME (3591): Duplicate window name
How to fix ER_WINDOW_DUPLICATE_NAME (3591) in MySQL window functions