Soluzione errore 1256 mysql "data truncated for column"
Quando ho scritto questo articolo, il 24 febbraio 2016, mi ero imbattuto in un errore durante l'import di un dump MySQL da un server di produzione verso un server di backup. Il numero di righe importate non corrispondeva al numero di righe sorgente, e l'analisi dei log mostrava decine di messaggi di errore relativi al "Data truncated for column" durante l'esecuzione dell'import. L'articolo originale documentava la diagnosi e la soluzione che avevo applicato all'epoca, ma a distanza di dieci anni, rileggendolo con occhio più allenato e con il fact-checking che oggi applico a ogni rewrite, devo correggere due imprecisioni tecniche significative dell'originale che, se replicate, porterebbero il lettore moderno a una risoluzione sbagliata.
La prima imprecisione è il codice errore. Il titolo riporta "errore 1256", ma il messaggio MySQL effettivo per "Data truncated for column" è il codice 1265 (WARN_DATA_TRUNCATED, SQLSTATE 01000). Il codice 1256 è invece ER_TOO_BIG_FOR_UNCOMPRESS ed è correlato a problemi di decompressione ZLIB, completamente non correlato alla truncation di dati di colonna. Lo slug dell'articolo storico è rimasto come era stato pubblicato nel 2016 perché è indicizzato da Google e linkato da terzi, ma il contenuto va corretto per essere tecnicamente accurato.
La seconda imprecisione, più importante, riguarda la causa reale dell'errore. L'articolo originale identificava come responsabile il parametro NO_AUTO_VALUE_ON_ZERO di sql_mode. È sbagliato: NO_AUTO_VALUE_ON_ZERO non c'entra nulla con la truncation, controlla esclusivamente come MySQL gestisce il valore zero su colonne AUTO_INCREMENT. La causa effettiva dell'errore 1265 è il sql_mode strict (STRICT_TRANS_TABLES o STRICT_ALL_TABLES), che promuove le warning di truncation a errori veri e propri abortendo lo statement. Questo aggiornamento documenta la diagnosi corretta che applico oggi quando gli stessi sintomi si manifestano nei progetti dei miei clienti.
Cos'è davvero l'errore "Data truncated for column" e quando si manifesta?
Il messaggio canonico è:
ERROR 1265 (01000) at line 683: Data truncated for column 'column_name' at row 10Lo SQLSTATE 01000 è la classe "Warning" del SQL standard. In condizioni di default, MySQL tratterebbe questa situazione come un avviso non bloccante, troncando il valore al limite della colonna e proseguendo. Quando però il sql_mode include STRICT_TRANS_TABLES o STRICT_ALL_TABLES, l'avviso viene promosso a errore vero e proprio, e lo statement viene abortito. Su tabelle InnoDB (transazionali), tutto lo statement viene rollback. Su tabelle MyISAM (non transazionali), la riga viene rifiutata e lo statement si interrompe.
Le cause concrete che fanno scattare il troncamento sono cinque, e vale la pena distinguerle perché la soluzione cambia.
Prima causa: lunghezza dati eccessiva rispetto alla dimensione della colonna. È il caso più frequente. Una colonna VARCHAR(50) riceve un valore di 60 caratteri. Senza strict mode, MySQL tronca a 50 e continua. Con strict mode, errore 1265 e abort.
Seconda causa: incompatibilità di character set. Una colonna definita con charset latin1 riceve dati in UTF-8 contenenti caratteri non rappresentabili. Il character byte sequence non si adatta al charset target, viene troncato al primo carattere problematico. Con UTF8 vs UTF8MB4 il problema è particolarmente sottile: i caratteri emoji e i caratteri CJK supplementari richiedono 4 byte per encoding, e una colonna utf8mb3 (max 3 byte per character) li tronca silenziosamente.
Terza causa: valori ENUM non validi. Una colonna ENUM('Attivo', 'Inattivo', 'Sospeso') riceve il valore 'Archiviato'. Con strict mode questo è un errore 1265 immediato. Senza strict mode, MySQL inserisce stringa vuota (indice 0) e genera warning silenziosa.
Quarta causa: precision loss su tipi numerici. Un valore 2.987654321 su una colonna DECIMAL(10,2) viene troncato a 2.99 (con rounding). MySQL nel sql standard considera questa una "data adjustment" non un "range error", quindi anche con strict mode non sempre genera 1265 (dipende dalla versione e dalla precisione del troncamento). È una sottigliezza importante: gli sviluppatori spesso assumono che strict mode catturi qualunque truncation, ma il troncamento numerico per fitting nel tipo target è un'operazione esplicitamente permessa dallo standard SQL.
Quinta causa: caratteri nascosti negli import CSV. Un caso particolarmente subdolo è l'import di CSV generati su Windows che usano \r\n come line terminator quando MySQL si aspetta \n puro. Il \r rimane attaccato al valore importato, e su colonne con vincoli specifici (es. ENUM, valori esatti) genera truncation perché il \r viene scartato in fase di matching.
Come si manifestava il problema nell'articolo originale del 2016 e qual è la diagnosi corretta?
Il contesto del 2016 era un import di dump MySQL da server di produzione (con sql_mode permissivo) verso server di backup (con sql_mode strict). Le righe della tabella sorgente contenevano valori che erano stati inseriti nel server di produzione senza i vincoli che il server di backup applicava. L'errore 1265 si manifestava perché il server di backup, in strict mode, rifiutava gli stessi dati che il server di produzione aveva accettato silenziosamente con truncation.
La causa specifica nei dati di quel caso era probabilmente una combinazione di lunghezze eccessive (campi VARCHAR con dati storici più lunghi della definizione corrente di colonna, accumulati nel tempo) e/o incompatibilità di charset (alcuni record con caratteri UTF-8 multibyte su colonne latin1). Senza il dump originale è impossibile dare una risposta certa retrospettivamente, ma il pattern di disallineamento configurazione produzione/backup è il classico generatore di questi sintomi.
La soluzione che avevo proposto nell'articolo originale (rimuovere un token da sql_mode sul server di backup) era sostanzialmente corretta nel principio (allineare le configurazioni) ma sbagliata nello specifico (il token responsabile non era NO_AUTO_VALUE_ON_ZERO ma STRICT_TRANS_TABLES o STRICT_ALL_TABLES). La soluzione operativa corretta è una di queste tre, in ordine di preferibilità ingegneristica.
Soluzione 1 (raccomandata): correggere i dati nella sorgente. Identificare le righe che eccedono i vincoli, fare una pulizia manuale o uno script di normalizzazione che riduce i valori al limite della colonna o estende la dimensione della colonna stessa. Ricreare il dump dalla sorgente pulita. Importare nel backup con strict mode invariato.
Soluzione 2: cambiare temporaneamente il sql_mode del backup per l'import. Inserire all'inizio del file dump (o eseguire prima dell'import) il comando SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO'; per disattivare strict mode solo per la sessione di import, lasciando la configurazione globale del server invariata. È un approccio più chirurgico della modifica permanente di my.cnf, e va bene per il backup perché lo strict mode resta attivo per le altre sessioni che lavorano sul server.
Soluzione 3: usare INSERT IGNORE. Modificare il dump per usare INSERT IGNORE invece di INSERT. Le truncation diventano warning anche in strict mode, l'import procede. Sconsigliata perché maschera il problema senza risolverlo, e in produzione lascia dati silenziosamente troncati che possono diventare bug funzionali in seguito.
La soluzione che applico oggi nei progetti di consulenza è quasi sempre la prima. Il principio operativo è: se il sql_mode del server di backup è strict per ragione (e dovrebbe esserlo, sotto MySQL 8 lo è di default), e il dump fallisce in import, il messaggio è che la sorgente contiene dati che non avrebbero dovuto essere accettati. Aggirare il problema rendendo il backup permissivo significa replicare nel backup gli stessi problemi di qualità dati della sorgente. La soluzione corretta è risalire alla sorgente, identificare quale costraint era stato indebolito o disabilitato in passato, e ripristinarlo dopo aver pulito i dati.
Perché NO_AUTO_VALUE_ON_ZERO esiste e cosa fa davvero?
Vale la pena chiarire questa modalità per evitare che la confusione del 2016 si propaghi. NO_AUTO_VALUE_ON_ZERO controlla esclusivamente il comportamento di colonne AUTO_INCREMENT quando ricevono il valore esplicito zero. Il default MySQL è: inserire zero genera il prossimo numero di sequenza esattamente come inserire NULL. Con NO_AUTO_VALUE_ON_ZERO attivo, inserire zero mantiene zero come valore letterale, e solo NULL genera il prossimo numero di sequenza.
Questa modalità è progettata specificamente per i dump MySQL. Se una tabella ha valori zero in colonne AUTO_INCREMENT (pratica sconsigliata ma a volte presente in codebase legacy), il dump generato da mysqldump include automaticamente all'inizio l'istruzione SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO'; proprio per preservare quei valori zero al reimport, evitando che MySQL li sostituisca con nuovi valori auto-generati.
NO_AUTO_VALUE_ON_ZERO non c'entra niente con la truncation di dati. È una protezione orthogonale, dedicata a un problema completamente diverso. La confusione nasce dal fatto che entrambi i token compaiono nel parametro sql_mode e che entrambi sono spesso citati nei contesti di dump/import.
Best practice 2026 per evitare disallineamenti tra ambienti?
Tre regole operative che applico nelle infrastrutture MySQL dei miei clienti italiani per evitare di trovarsi in situazioni come quella del 2016.
Prima regola: standardizza il sql_mode tra tutti gli ambienti. Sviluppo, staging, backup, produzione devono condividere lo stesso sql_mode. La policy di default che applico è STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO. È la configurazione più vicina al SQL standard e cattura in fase di sviluppo i problemi di qualità dati che altrimenti si accumulano in produzione fino a manifestarsi durante un upgrade o una migrazione. La documentazione ufficiale MySQL su sql_mode è il riferimento autoritativo per ogni token disponibile.
Seconda regola: charset unificato a UTF8MB4 ovunque. Il vecchio utf8 di MySQL (alias di utf8mb3) supporta solo caratteri Unicode fino al BMP (Basic Multilingual Plane) e tronca emoji, simboli matematici avanzati, ideogrammi CJK supplementari, alcuni caratteri storici. Il moderno utf8mb4 supporta l'intero Unicode. Per progetti nuovi nel 2026 non c'è ragione tecnica per usare altro che utf8mb4 come charset di default, sia a livello server (character_set_server) sia a livello tabella e colonna. La migrazione da utf8 a utf8mb4 su database esistenti è uno degli interventi più frequenti che faccio negli audit di diagnosi delle connessioni lente MySQL su VPS e di modernizzazione di stack legacy.
Terza regola: strategia di backup con xtrabackup invece di mysqldump testuale. Per database di dimensioni rilevanti (sopra i 10 GB) o per applicazioni mission-critical, il dump testuale con mysqldump diventa lento e suscettibile esattamente a queste classi di problemi. La strategia che documento nella mia guida ai backup incrementali MySQL con xtrabackup senza blocchi sposta il problema su un livello diverso: backup binari coerenti, recovery point granulari, nessun re-execution di INSERT che possa fallire per truncation.
Quarta regola: validazione dei dati a livello applicativo, non solo a livello database. Una pratica che applico nei progetti PHP/Laravel moderni che gestisco è non delegare al database la validazione di lunghezza, formato e charset. La validazione deve avvenire prima dell'INSERT, nel boundary applicativo (FormRequest in Laravel, Constraint in Symfony, validatori custom). Il database resta come ultima linea di difesa per integrità referenziale e vincoli strutturali, ma i vincoli di lunghezza e di tipo dovrebbero essere catturati dall'applicazione con messaggi di errore espliciti per l'utente. Questo riduce significativamente la superficie di problemi che si manifestano come errore 1265 in produzione, perché i dati che arrivano al database sono già normalizzati. È un pattern che richiede disciplina iniziale ma elimina una classe intera di bug operativi che altrimenti si scoprono solo quando un import o una migrazione fallisce.
Una nota su MySQL 8.0 e versioni successive: dalla versione 8.0 (rilasciata aprile 2018) lo strict mode è diventato il default operativo. La configurazione fuori dalla scatola include STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION come sql_mode di default. Significa che ogni installazione MySQL 8 fresca ha già strict mode attivo, e il comportamento di promozione warning-to-error è il comportamento standard. Per le codebase PHP/Laravel/Symfony moderne questo è generalmente desiderabile, ma ha conseguenze nei progetti di migrazione da MySQL 5.6/5.7 a 8.0: codice che funzionava silenziosamente con truncation sotto 5.7 inizia a fallire con errore 1265 sotto 8.0. È una delle tre fonti più frequenti di problemi che vedo nelle migrazioni MySQL 5.7 → 8.0, ed è esattamente la classe di problemi che lo strict mode è progettato per esporre. La soluzione corretta non è disabilitare strict mode dopo l'upgrade, ma cogliere l'occasione per pulire i dati e i vincoli che strict mode sta giustamente segnalando.
Le risorse autorevoli che continuo a consultare per la diagnostica MySQL sono il manuale di riferimento ufficiale degli error code, la pagina specifica sul sql_mode su dev.mysql.com, e per casi specifici i bug report ufficiali su bugs.mysql.com che spesso documentano edge case che la documentazione narrativa non copre.
Per chi gestisce database MySQL in produzione e si trova periodicamente con problemi di qualità dati che emergono solo in fase di import o di migrazione fra ambienti, il mio profilo professionale include esperienze concrete su audit della configurazione MySQL, allineamento di sql_mode e charset tra ambienti, strategie di backup incrementale per database con vincoli di RPO stretti, e migrazione di dati legacy con normalizzazione automatica. Se la tua organizzazione gestisce un database MySQL con questi tipi di sintomi (import che falliscono, valori troncati, disallineamenti silenziosi tra produzione e backup), contattami direttamente per inquadrare un audit specifico. La diagnosi richiede tipicamente accesso ai log slow query, alla configurazione my.cnf di tutti gli ambienti, e a un campione rappresentativo dei dati. Da lì si costruisce un piano di pulizia e standardizzazione che, una volta completato, elimina la classe intera di problemi di cui questo articolo del 2016 era un esempio.