Bad control character in string literal
A string contains a character JSON only accepts in escaped form. The rule covers the 32 control characters, U+0000 to U+001F, and in practice the culprit is almost always a tab or a line break typed or pasted straight into a value. The document can look perfectly fine, because the character is whitespace.
Input
{"name": "Ada Lovelace"}Do
- Paste the input into the validator.
- Press Repair in the editor band.
Result
Escaped control characters
{"name": "Ada\tLovelace"}| Runtime | Message |
|---|---|
| Chrome 153 · Node.js 26 | Bad control character in string literal in JSON at position 13 (line 1 column 14) |
| Node.js 18 (older V8) | Unexpected token ␉ in JSON at position 13 |
| Firefox 156 | JSON.parse: bad control character in string literal at line 1 column 14 of the JSON data |
| Safari 26 | JSON Parse error: Unterminated string |
| Python 3.14 json | Invalid control character at: line 1 column 14 (char 13) |
| This site | Unescaped control character — write it as \u0009line 1, column 14 |
␉ stands for a tab. Recorded from each runtime; the wording is in Firefox’s, Safari’s and Python’s sources.
Why JSON refuses a real tab
The JSON standard, RFC 8259, says that inside a string every control character must be escaped: a tab as \t, a line break as \n, a carriage return as \r, and any other one as \u followed by four hex digits. Keeping them out means a JSON string can never be split across lines by accident, and a reader can always find where it ends. So "Ada\tLovelace" written with a backslash and a t is fine, while the same text with an actual tab between the names is not. In the example the invisible tab sits between Ada and Lovelace, and the table marks it with a symbol so you can see where it was.
Only those 32 characters are affected. Accented letters, emoji, Chinese or Arabic text and every other printable character can sit in a JSON string exactly as typed, and so can U+007F, the delete character, which the standard leaves alone. So the error never means that your text is in the wrong language or needs converting to ASCII. It means one specific invisible character, and the position tells you where to look for it.
How it gets there
- JSON assembled from strings.
'{"note": "' + text + '"}'works until the text holds a tab, a line break or a quote. This is the most common cause by far. - Cells copied from a spreadsheet, which arrive separated by tabs, pasted into the middle of a value.
- A multi-line value in a hand-edited file, where someone pressed Enter inside a long description to keep it readable.
- Templates that drop user input into a JSON skeleton without escaping it.
The fix at the source is the same in every case: let an encoder write the string. JSON.stringify in JavaScript and json.dumps in Python escape every control character for you. Python can also read such text as it is, with json.loads(text, strict=False), but that only moves the problem to the next program that reads the file.
A line break is different
A raw line break raises the same error in Chrome, Node.js, Firefox and Python, and Safari calls it an unterminated string. What changes is how a repair tool can read it:
Input
{"note": "first line
second line"}Do
- Paste the input into the validator.
- Press Repair in the editor band.
Result
Closed unterminated strings
Inserted missing commas
Inserted missing colons
Quoted bare property names
Quoted bare values
{"note": "first line",
"second" :"line\""}| Runtime | Message |
|---|---|
| Chrome 153 · Node.js 26 | Bad control character in string literal in JSON at position 20 (line 1 column 21) |
| Node.js 18 (older V8) | Unexpected token ␊ in JSON at position 20 |
| Firefox 156 | JSON.parse: bad control character in string literal at line 1 column 21 of the JSON data |
| Safari 26 | JSON Parse error: Unterminated string |
| Python 3.14 json | Invalid control character at: line 1 column 21 (char 20) |
| This site | Unescaped control character — write it as \u000aline 1, column 21 |
␊ stands for a line break. Recorded from each runtime; the wording is in Firefox’s, Safari’s and Python’s sources.
Repair assumes a line break inside a string means the closing quote was forgotten, which is the right guess for a string that someone left open by mistake. It keeps the damage to one line instead of swallowing the rest of the document. For a value that was meant to span two lines, that guess is wrong: the result above parses, but second has become a key and the note has lost its second line. The list of fixes gives it away, with five kinds of change for what should have been one character. Undo, and write \n where the line break was.
Fixing it here
The JSON Validator underlines the exact character, and its message says how to write it: the table's last row names the escape, \u0009 for the tab. Repair escapes a tab or any other control character in place, as the first example shows, and it leaves the rest of the document untouched. For line breaks, check the result before you keep it. When the document came out of your own code, the lasting fix is still to build it with an encoder. The guide to fixing invalid JSON lists the other repairs and how to read what Repair reports.