SyntaxError: Bad control character in string literal in JSON at position N significa che c'è un carattere di controllo grezzo e non escape —— una tab, una nuova riga, un carriage return, o qualunque carattere nell'intervallo U+0000–U+001F —— seduto dentro a una stringa JSON. La specifica JSON li proibisce. Questo articolo spiega perché, da dove vengono, e come liberartene.
Quale errore di stringa sto ricevendo?
- Bad control character —— un tab/nuova riga/byte null grezzo è dentro a una stringa senza escape.
- Bad escaped character —— una
\è seguita da qualcosa che JSON non permette (es.\x, path di Windows). - Unterminated string —— una stringa è stata aperta con
"ma mai chiusa.
Cos'è un carattere di controllo?
I primi 32 code point Unicode (U+0000 fino a U+001F) sono caratteri di controllo —— codici di formattazione invisibili ereditati dalle telescriventi. Alcuni familiari:
| Code point | Nome | Escape JSON |
|---|---|---|
U+0000 | Null | |
U+0008 | Backspace | \b |
U+0009 | Tab orizzontale | \t |
U+000A | Line feed (nuova riga) | \n |
U+000C | Form feed | \f |
U+000D | Carriage return | \r |
U+001B | Escape (le sequenze ANSI iniziano qui) | |
La specifica JSON (RFC 8259 §7) è esplicita: tutti i caratteri in questo intervallo devono essere escape quando appaiono dentro una stringa JSON. Una nuova riga grezza dentro una stringa tra virgolette è un errore di sintassi, non un valore.
Perché le nuove righe grezze rompono JSON
Considera un valore di stringa che contiene un vero carattere di nuova riga:
// Come appaiono i byte (la nuova riga è letterale, non \n):
{"message":"line one
line two"}
JSON.parse('{"message":"line one\nline two"}')
// SyntaxError: Bad control character in string literal in JSON at position 20Il parser vede la doppia virgoletta di apertura, legge caratteri, poi incontra un byte 0x0A grezzo. Visto che una stringa JSON dev'essere una singola sequenza ininterrotta tra due doppie virgolette sulla stessa riga logica, la nuova riga nuda termina la stringa in modo inatteso.
La rappresentazione corretta usa la sequenza di escape a due caratteri \n:
// Corretto —— \n è l'escape, non una nuova riga letterale
{"message":"line one\nline two"}
JSON.parse('{"message":"line one\nline two"}') // ✓ funzionaCome i caratteri di controllo finiscono nel JSON
1. Costruire stringhe JSON con template literal o concatenazione
const note = `first line
second line`; // vera nuova riga dal template literal
const json = `{"note":"${note}"}`; // nuova riga grezza incorporata nella stringa
JSON.parse(json); // SyntaxError: Bad control character…
// ✓ Soluzione: usa sempre JSON.stringify per i valori, non interpolare mai a mano
const json = JSON.stringify({ note }); // fa escape della nuova riga automaticamente2. Leggere file e incorporare contenuto così com'è
import fs from 'fs';
const content = fs.readFileSync('notes.txt', 'utf8');
// content può contenere \n, \r\n, \t, ecc.
// ❌ Costruzione manuale di stringa —— incorpora caratteri di controllo grezzi
const json = '{"content":"' + content + '"}';
// ✓ JSON.stringify gestisce tutti gli escape
const json = JSON.stringify({ content });3. Risposte API da sistemi che non fanno escape dell'output
Alcuni sistemi back-end (script PHP legacy, serializer custom, trigger di database che costruiscono JSON manualmente) emettono valori di campo con nuove righe o tab non escape. Ricevi JSON sintatticamente invalido e response.json() lancia.
Diagnostica loggando la lunghezza del body grezzo e i primi 200 caratteri. Cerca interruzioni di riga visibili dentro a quello che dovrebbe essere un valore di stringa.
4. Copia-incolla da un terminale (sequenze di escape ANSI)
L'output del terminale contiene codici colore ANSI che iniziano col carattere Escape (0x1B) seguito da [. Se incolli l'output del terminale in una stringa JSON, ogni reset di colore (\x1B[0m) diventa un carattere di controllo.
// Output del terminale incollato in una stringa JSON:
{"log":"\u001B[32mOK\u001B[0m request processed"}
// ^^^ byte ESC grezzo —— dovrebbe essere la sequenza di escape \u001B di 6 caratteri5. Byte null da dati binari
Leggere parte di un file binario, una colonna BLOB di database, o una stringa stile C dentro un campo JSON può incorporare byte null (U+0000), che sono la fonte più comune di questo errore nelle pipeline da DB a JSON.
Come risolverlo
Prevenzione: usa sempre JSON.stringify
Questa è la soluzione definitiva. JSON.stringify() fa escape correttamente di ogni carattere di controllo —— non devi mai fare escape a mano.
// ✓ Sicuro indipendentemente da cosa contenga userInput
const json = JSON.stringify({ message: userInput });Riparazione: rimuovi o fai escape a posteriori
Se stai ricevendo JSON rotto da una fonte esterna e non puoi sistemare il produttore, puoi sanificare la stringa grezza prima del parsing. L'approccio più sicuro è fare escape dei caratteri di controllo nudi:
function escapeControlChars(raw) {
return raw.replace(
/[\u0000-\u001F]/g,
(ch) => '\\u' + ch.charCodeAt(0).toString(16).padStart(4, '0')
);
}
const fixed = escapeControlChars(rawFromApi);
const data = JSON.parse(fixed);Se i caratteri di controllo sono rumore (codici ANSI, byte null) anziché dati significativi, rimuoverli è più semplice:
// Rimuove tutti i caratteri di controllo eccetto \t, \n, \r (comuni nel testo)
const cleaned = raw.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F]/g, '');
const data = JSON.parse(cleaned);Altre due fonti reali
- BOM UTF-8 in posizione 0. I file salvati come «UTF-8 con BOM» iniziano con
0xEF 0xBB 0xBF. RFC 8259 proibisce un BOM in JSON, eJSON.parselo rifiuta con un errore in posizione 0 facile da confondere con un carattere di controllo. Rimuovilo context.replace(/^ /, '')prima del parsing, o salva il file come «UTF-8» (senza BOM) nel tuo editor. - VS Code: «Remove Control Characters». Se stai pulendo un singolo file a mano,
Selection → Remove All Occurrences of Find Matchdi VS Code combinato con ricerca regex su[\x00-\x08\x0b\x0c\x0e-\x1f](preservando i veri\t\n\r) cancella i byte di controllo invisibili in un colpo solo. C'è anche un comando «Remove Control Characters» in alcune estensioni se lo fai spesso.
Individuare la posizione incriminata
Il messaggio di errore include una posizione in byte. Usala per ispezionare cosa c'è lì:
try {
JSON.parse(raw);
} catch (e) {
const pos = Number(e.message.match(/position (\d+)/)?.[1]);
if (!isNaN(pos)) {
const context = raw.slice(Math.max(0, pos - 20), pos + 20);
console.log('Context around error:', JSON.stringify(context));
// JSON.stringify rifà l'escape dei caratteri di controllo così li vedi
// es. "...line one\nline two..." —— quel \n era una nuova riga grezza
}
}Domande frequenti
Cos'è un bad control character in JSON?
Un carattere grezzo non-escape nell'intervallo U+0000–U+001F —— un tab, nuova riga, carriage return, byte null, o escape ANSI —— dentro a una stringa JSON. RFC 8259 §7 richiede che ognuno di tali caratteri sia escape (per esempio \n per una nuova riga).
Come risolvo un errore bad control character?
La soluzione definitiva è costruire il JSON con JSON.stringify(), che fa escape dei caratteri di controllo automaticamente. Se ricevi JSON rotto che non puoi sistemare alla sorgente, fai escape o rimuovi i caratteri di controllo con una regex prima del parsing (vedi snippet sopra).
Perché una nuova riga grezza rompe JSON?
Una stringa JSON dev'essere una sequenza ininterrotta tra due doppie virgolette. Un byte letterale di nuova riga (0x0A) termina la stringa prematuramente, perciò il parser segnala un errore di carattere di controllo. La rappresentazione valida è l'escape a due caratteri \n.
Come rimuovo i codici colore ANSI dal JSON?
Le sequenze ANSI iniziano col byte Escape (U+001B). Rimuovile con raw.replace(/\[[0-9;]*m/g, '') prima del parsing, o evita di incollare output grezzo del terminale dentro valori di stringa JSON in prima battuta.
Ripara JSON rotto nel tuo browser
Incolla il tuo JSON rotto in JSON Fix su fixjson.org. Il parser tollerante identifica le violazioni di carattere di controllo e segnala la posizione esatta. Per i casi semplici può riparare la stringa automaticamente.
- JSON Fix —— valida, ripara e formatta JSON non valido
- Risolvere «[object Object] is not valid JSON» —— la guida completa agli errori di sintassi JSON
- Come risolvere gli errori JSON.parse 'Unexpected Token' —— ogni variante del SyntaxError e la sua causa
- Unexpected end of JSON input —— quando la struttura viene tagliata prima di chiudersi
- Unexpected token o spiegato —— quando passi un oggetto invece di una stringa a JSON.parse