json-errorjson-parseescapingcontrol-characterdebugging

Bad Control Character in String Literal in JSON: Cause & Fix

·7 min read·Error Fixes

What the error actually means

SyntaxError: Bad control character in string literal in JSON (Chrome/V8) — or "Invalid control character" / "Unexpected control character" in other engines — means a raw control character sits inside a JSON string where it isn't allowed. The JSON spec forbids unescaped control characters (code points U+0000 through U+001F) inside string values. The usual culprits are a literal newline, a tab, or a carriage return that ended up *inside the quotes* instead of being written as an escape sequence.

The parser is very specific here: the JSON *structure* is fine, but one string value contains a byte it isn't allowed to contain literally. This is different from Unexpected token at position 0 (which usually means you got HTML) and Unexpected end of JSON input (truncated data) — this one is about escaping.

The classic reproduction

A literal line break inside a string value triggers it immediately:

js
// ❌ raw newline inside the string → "Bad control character"
JSON.parse('{ "note": "line one
line two" }');

JSON requires that newline to be the two-character escape \n, not an actual line break:

js
// ✅ escaped newline → parses fine
JSON.parse('{ "note": "line one\nline two" }');   // { note: "line one
line two" }

Inside a JSON string, these characters must be escaped:

CharacterMust be written as
Newline\n
Carriage return\r
Tab\t
Backspace\b
Form feed\f
Any other control char (U+0000–U+001F)\uXXXX

Where it comes from in real code

You rarely type a raw newline on purpose — it sneaks in:

  • Pasting multi-line text (a log excerpt, a stack trace, a code snippet, an address) directly into a .json file or a string literal. The line breaks you see are real newline bytes.
  • Building JSON by hand with string concatenation — e.g. '{"msg":"' + userText + '"}'. If userText contains a newline or tab, you've produced invalid JSON. This is the number-one cause and the reason you should never assemble JSON with string joining.
  • Copying data from a spreadsheet or terminal, which carries tabs and carriage returns you can't see.
  • Windows line endings (\r\n) in text that gets embedded into a string value.

The right fix: let a serializer do the escaping

If you're generating the JSON, never build it by hand — use JSON.stringify, which escapes control characters correctly and automatically:

js
// ✅ stringify handles the escaping for you
const note = "line one
line two	with a tab";
const json = JSON.stringify({ note });
// '{"note":"line one\nline two\twith a tab"}'  → always valid
JSON.parse(json);   // round-trips cleanly

The moment you find yourself concatenating strings to make JSON, stop and use JSON.stringify (or your language's equivalent — Python's json.dumps, Go's json.Marshal). They exist precisely to get escaping right.

Fixing JSON you received

If the broken JSON came from *someone else* and you can't change the producer:

  1. Find the offending value. The error often reports a position; format the raw text and look for a string that visibly wraps onto a new line inside its quotes.
  2. Escape the control characters — replace real newlines with \n, tabs with \t. The JSON escape tool does this for a pasted value.
  3. Re-validate with a JSON validator to confirm the string is now clean, and try the JSON fixer for a quick automated repair of common escaping issues.

Prevention checklist

  • Generate JSON with a serializer, never with string concatenation or template literals.
  • Escape user-supplied text before it enters a JSON string if you must handle it manually.
  • Normalize line endings on ingest if you embed multi-line text.
  • Validate at the boundary so malformed JSON is caught on entry, not three functions later.

For the broader set of parse failures see the JSON parse errors guide and common JSON mistakes.

Frequently asked questions

A raw control character — most often a literal newline, carriage return, or tab — is sitting inside a JSON string value. JSON forbids unescaped control characters (U+0000–U+001F) inside strings, so the parser rejects it.

Replace the literal line break with the escape sequence \n (and tabs with \t, carriage returns with \r). Better still, generate the JSON with JSON.stringify, which produces those escapes automatically.

JSON.stringify walks your data and escapes every control character correctly as it serializes, so the output is always valid JSON. The error almost always comes from building JSON by hand with string concatenation instead of using a serializer.

Newline (\n), carriage return (\r), tab (\t), backspace (\b), form feed (\f), the double quote (\"), and the backslash (\\). Any other control character in the U+0000–U+001F range must be written as a \uXXXX escape.

No. "Unexpected token" usually means the parser hit something that isn't valid JSON structure — often an HTML error page instead of JSON. "Bad control character" means the structure is fine but one string value contains an unescaped control character.

Try JSONKit — JSON Developer Tools

Format, validate, convert and diff JSON. Instant, accurate and 100% private.