Why it happens
Inside quotes, JSON accepts every character except the 32 control characters U+0000 to U+001F, which must be escaped: a line break as \n, a tab as \t, the rest as \u plus four hex digits. The sample breaks this rule on line 4. Most common sources first:
1. Multi-line text dropped into a JSON template. Text from a textarea, an email or a commit message, inserted with a template literal or +:
const json = `{"ticket": 4471, "body": "${form.body.value}"}`; // breaks on Enter, quotes, backslashes
Language models also sometimes write real line breaks inside long string values.
2. JSON inside a string literal in your code. JavaScript and Python turn \n into a real line break before JSON.parse sees it:
JSON.parse('{"status":"paid\nshipped"}'); // SyntaxError: the \n became a real line break
3. Tabs from spreadsheets. Cells copied from Excel or Google Sheets carry tabs into the value. The checker names them: Raw tab inside a string.
4. Invisible control characters. Terminal color codes (ESC, U+001B) captured from command output, or NUL bytes from binary data. The checker reports the code point, for example Raw control character U+001B inside a string.
Node.js 18 and older Chrome reported this as Unexpected token, then the raw character itself, then in JSON at position 65, so a line break split the message in two.
How to fix a bad control character in a string literal
Build JSON with a serializer
const ticket = { ticket: 4471, subject: 'Refund request', body: form.body.value };
const json = JSON.stringify(ticket); // the line break becomes \n
json.dumps(ticket) does the same in Python.
Keep the backslash in string literals
JSON.parse('{"status":"paid\\nshipped"}');
JSON.parse(String.raw`{"status":"paid\nshipped"}`);
In Python, use a raw string: json.loads(r'{"status": "paid\nshipped"}').
Clean text you do not control
In Python, json.loads(text, strict=False) accepts raw control characters in strings, and json.dumps writes them back escaped. In JavaScript, first remove the ones that are never valid anywhere in JSON (all but tab, line feed and carriage return):
const cleaned = text.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F]/g, '');
Raw line breaks and tabs are only wrong inside strings, which search and replace cannot tell apart. JSON Repair escapes them there, along with carriage returns, form feeds and backspaces; it stops at other control characters, so run the cleanup first. For a single value, the JSON escape tool turns raw text into a valid JSON string.
Color codes from command output
Turn colors off at the source (many tools honor NO_COLOR=1 or a --no-color flag), or strip captured codes with text.replace(/\u001b\[[0-9;]*m/g, '').
Same error in other languages
- Python:
json.decoder.JSONDecodeError: Invalid control character at: line 4 column 15 (char 65) - Go:
invalid character '\n' in string literal - PHP:
json_decode()returnsnull, andjson_last_error_msg()saysControl character error, possibly incorrectly encoded. - A missing closing quote is a different problem: Unterminated string in JSON.
Paste failing text into the JSON validator to find the character; common JSON errors covers related problems.