apache/druid · error · ParseException
Unable to parse row [%s]
Error message
Unable to parse row [%s]
What it means
JSONToLowerParser.parseToMap parses a JSON row and lowercases all keys (and optionally values) so ingestion is case-insensitive. Any failure while parsing the JSON or building the lowered map is wrapped in a ParseException with the raw input text as the message detail.
Source
Thrown at processing/src/main/java/org/apache/druid/java/util/common/parsers/JSONToLowerParser.java:147
final List<Object> nodeValue = Lists.newArrayListWithExpectedSize(node.size());
for (final JsonNode subnode : node) {
final Object subnodeValue = VALUE_FUNCTION.apply(subnode);
if (subnodeValue != null) {
nodeValue.add(subnodeValue);
}
}
map.put(StringUtils.toLowerCase(key), nodeValue); // difference from JSONParser parse()
} else {
final Object nodeValue = VALUE_FUNCTION.apply(node);
if (nodeValue != null) {
map.put(StringUtils.toLowerCase(key), nodeValue); // difference from JSONParser parse()
}
}
}
return map;
}
catch (Exception e) {
throw new ParseException(input, e, "Unable to parse row [%s]", input);
}
}
}
View on GitHub (pinned to 9b90983fd2)
Solutions
- Ensure each input record is a complete, valid JSON object before parsing.
- Fix upstream producers so records are not truncated or concatenated.
- Use the appropriate parser type for the actual payload format.
- Skip or quarantine rows that are empty/null before calling the parser.
- Catch ParseException per-row in ingestion code and log/forward the bad record instead of aborting.
Example fix
// before
Map<String, Object> row = parser.parseToMap(record.value()); // may throw ParseException
// after
String input = record.value();
try {
Map<String, Object> row = parser.parseToMap(input);
} catch (ParseException e) {
log.warn(e, "Bad row, skipping: %s", input);
}
Defensive patterns
Strategy: try-catch
Validate before calling
private static boolean isParsableJsonObject(final ObjectMapper mapper, final String input) {
if (input == null || input.trim().isEmpty()) { return false; }
try {
return mapper.readTree(input).isObject();
} catch (IOException e) { return false; }
} Try / catch
try {
Map<String, Object> row = parser.parseToMap(input);
} catch (ParseException e) {
log.warn(e, "Bad row skipped: %s", input);
} Prevention
- Ensure records are complete JSON objects (no mid-line splits in NDJSON).
- Reject null/empty messages before parsing.
- Use the correct parser for the payload format.
- Route parse failures to a reject/dead-letter path instead of failing ingestion.
When it happens
Trigger: Calling parseToMap(String) with input that is not a valid JSON object (malformed JSON, empty/null string, non-object top-level value whose handling breaks key lowering), or an element whose processing (e.g. flattening nested maps) throws.
Common situations: Feeding newline-delimited JSON where a record was split mid-object; passing plain-text or CSV rows to a JSON parser; null/empty messages from a message bus; numeric or array top-level JSON values instead of objects.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Unable to parse row [%s]
- Illegal segmentId format [%s]
- JavaScript parsed value [%s] must be in {key: value} format!
- Value [%s] is not valid for property [%s]
- Unable to parse line.
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/c506d82a54a0edd4.
Report an issue: GitHub.