Invalid control character at: line L column C (char N)
A string contains a literal character below U+0020, usually a real newline or tab. JSON requires those to be escaped. Python has a strict=False option that accepts them, but the resulting document is still invalid for every other consumer.
Paste your JSON and see exactly where it breaks
Nothing you paste leaves your browser. The connect-src allowlist makes that a browser guarantee rather than a promise. Check it yourself
What actually causes it
Ranked by how often each one turns out to be the answer.
-
01 Multi-line text inside a string
Log excerpts, SQL, stack traces and templated HTML all carry real newlines.
Breaks
json.loads('{"sql": "SELECT * FROM t"}')Works
json.loads('{"sql": "SELECT *\\nFROM t"}') -
02 JSON assembled with f-strings or concatenation
Interpolated values are not escaped. Build a dict and use json.dumps, which escapes everything correctly.
Breaks
body = f'{{"note": "{note}"}}'Works
body = json.dumps({"note": note}) -
03 A tab copied from a spreadsheet
Invisible, and it survives copy and paste intact.
The same mistake in other runtimes
The underlying problem is identical; only the wording differs. If a colleague reports one of these, they are looking at the same thing you are.
| JavaScript (V8) | Bad control character in string literal in JSON at position 11 (line 1 column 12) |
|---|
Questions
- What does strict=False do?
- json.loads(s, strict=False) allows control characters inside strings. It gets you moving, but the document remains invalid JSON, so anything downstream that is not Python will still reject it. Fix the producer instead.
Fix it now
Paste the payload into the tool above, or go straight to the one built for this job.
Escape the string properly