quarkusio/quarkus · error · IllegalArgumentException
Control characters not allowed in json string
Error message
Control characters not allowed in json string
What it means
While scanning the characters of a JSON string literal, readString() rejects any character for which Character.isISOControl(ch) is true (control characters such as \n, \t, \r, \0 inside the raw literal). The JSON spec requires such characters to be escaped as \uXXXX sequences, so the reader throws this IllegalArgumentException instead of accepting them.
Source
Thrown at core/builder/src/main/java/io/quarkus/builder/JsonReader.java:207
* |----- 'r'
* |----- 't'
* |----- 'u' hex hex hex hex
*/
private JsonString readString() {
position++;
int start = position;
// Substring on string values that contain unicode characters won't work,
// because there are more characters read than actual characters represented.
// Use StringBuilder to buffer any string read up to unicode,
// then add unicode values into it and continue as usual.
StringBuilder unicodeString = null;
while (position < length) {
final int ch = nextChar();
if (Character.isISOControl(ch)) {
throw new IllegalArgumentException("Control characters not allowed in json string");
}
if ('"' == ch) {
final String chunk = text.substring(start, position - 1);
final String result = unicodeString != null
? unicodeString.append(chunk).toString()
: chunk;
// End of string
return new JsonString(result);
}
if ('\\' == ch) {
switch (nextChar()) {
case '"': // quotation mark
case '\\': // reverse solidus
case '/': // solidus
case 'b': // backspaceView on GitHub (pinned to e1c734241f)
Solutions
- Replace raw control characters inside string literals with their JSON escapes: \n, \r, \t, \u0000, etc.
- Generate the JSON with a serializer (Jackson, jakarta.json) instead of hand-concatenating strings so escaping is automatic.
- Sanitize the input by stripping or escaping all characters below 0x20 before parsing.
- Check the source of the text (file/editor/terminal) for encoding issues that inject control bytes.
Example fix
// before (real newline byte inside the literal)
String json = "{\"msg\":\"line1
line2\"}";
// after
String json = "{\"msg\":\"line1\\nline2\"}"; Defensive patterns
Strategy: validation
Validate before calling
String sanitize(String json) {
StringBuilder sb = new StringBuilder(json.length());
boolean inStr = false;
for (int i = 0; i < json.length(); i++) {
char c = json.charAt(i);
if (c == '"' && (i == 0 || json.charAt(i - 1) != '\\')) inStr = !inStr;
if (inStr && c < 0x20) {
sb.append(String.format("\\u%04x", (int) c));
} else {
sb.append(c);
}
}
return sb.toString();
}
// parse sanitize(input) instead of input Try / catch
try {
JsonReader.parse(json);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Control characters")) {
throw new ConfigException("Raw control character inside a JSON string; escape as \\n, \\t, or \\uXXXX", e);
}
throw e;
} Prevention
- Always escape control characters in string literals (\n, \r, \t, \u00XX)
- Use a JSON serializer rather than hand-building strings
- Check input sources (logs, terminals, editors) for injected control bytes
When it happens
Trigger: Calling the reader with JSON where an unescaped raw control character (newline, tab, carriage return, NUL, etc.) appears inside a quoted string, e.g. "{\"key\":\"line1\nline2\"}" with a literal byte 0x0A instead of the two-character escape \\n.
Common situations: Strings built via string concatenation or templates in scripts that embed real newlines/tabs instead of escapes; JSON assembled manually in shell or config; data copied from logs or terminals that contain control bytes; binary-corrupted input files.
Related errors
- Control characters not allowed in json string
- Json array ended without ]
- String not closed
- Unable to read json constant for: %s
- Unable to fully read json value
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/94798cf0facb5723.
Report an issue: GitHub.