Illegal unquoted character ((CTRL-CHAR, code 10))
A string in the document contains a literal control character. Code 10 is a newline, code 13 a carriage return, code 9 a tab. JSON requires these to be escaped, and Jackson is right to reject them. This is the same underlying problem V8 calls a bad control character.
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 embedded in a string
SQL, stack traces, log lines and templated content all carry real newlines.
Breaks
{ "query": "SELECT * FROM users" }Works
{ "query": "SELECT *\nFROM users" } -
02 JSON built by concatenation
Interpolated values are not escaped. Serialise with ObjectMapper.writeValueAsString instead of assembling the text by hand.
Breaks
String body = "{\"note\": \"" + note + "\"}";Works
String body = mapper.writeValueAsString(Map.of("note", note)); -
03 Windows line endings inside a value
Carriage return, code 13, is a separate control character and needs \r.
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) |
|---|---|
| Python | Invalid control character at: line 1 column 8 (char 7) |
Questions
- What about ALLOW_UNESCAPED_CONTROL_CHARS?
- JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS makes Jackson accept them, and it is a reasonable stopgap when you cannot change the producer. It does not make the document valid JSON, so anything else consuming it will still fail.
Fix it now
Paste the payload into the tool above, or go straight to the one built for this job.
Escape the value