apache/druid · error · ParseException

Unable to parse row [%s]

Error message

Unable to parse row [%s]

What it means

JSONPathParser.parseToMap parses an input string as JSON with Jackson and flattens the resulting JsonNode into a Map using a JSONPath/JSON-flattener spec. If Jackson cannot parse the input as JSON, or the flattening step fails, the parser wraps the cause in a ParseException reporting the offending raw row.

Source

Thrown at processing/src/main/java/org/apache/druid/java/util/common/parsers/JSONPathParser.java:77

  {
  }

  /**
   * @param input JSON string. The root must be a JSON object, not an array.
   *              e.g., {"valid": "true"} and {"valid":[1,2,3]} are supported
   *              but [{"invalid": "true"}] and [1,2,3] are not.
   *
   * @return A map of field names and values
   */
  @Override
  public Map<String, Object> parseToMap(String input)
  {
    try {
      JsonNode document = mapper.readValue(input, JsonNode.class);
      return flattener.flatten(document);
    }
    catch (Exception e) {
      throw new ParseException(input, e, "Unable to parse row [%s]", input);
    }
  }
}

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Validate and fix the input data: ensure each record is a single, well-formed JSON object.
  2. Verify the ingestion spec uses the correct parser (e.g. 'string' JSON parser, not JSONPath) for the actual data format.
  3. Check for encoding/truncation issues at the source (char-set mismatch, partial reads).
  4. If flatten spec fields don't match the document shape, correct the flattenSpec (useFieldDiscovery, paths).
  5. Catch ParseException in the ingestion path and route bad rows to a dead-letter/sink instead of failing the task.

Example fix

// before
Map<String, Object> row = new JSONPathParser(flattenSpec).parseToMap(rawLine); // rawLine may be "{a:1}{b:2}"
// after
ObjectMapper mapper = new ObjectMapper();
if (rawLine == null || rawLine.trim().isEmpty()) { continue; }
try {
  mapper.readTree(rawLine); // pre-validate
} catch (IOException e) { sendToDeadLetter(rawLine); continue; }
Map<String, Object> row = parser.parseToMap(rawLine);
Defensive patterns

Strategy: try-catch

Validate before calling

private static boolean isValidJson(final ObjectMapper mapper, final String input) {
  if (input == null || input.trim().isEmpty()) { return false; }
  try { mapper.readTree(input); return true; } catch (IOException e) { return false; }
}

Try / catch

try {
  Map<String, Object> row = parser.parseToMap(input);
} catch (ParseException e) {
  log.warn(e, "Skipping unparseable row: %s", input);
  // forward to dead-letter topic / reject report
}

Prevention

When it happens

Trigger: Calling parseToMap(String) (or the generic Parser interface used by ingestion) with input that is not valid JSON (truncated line, concatenated JSON objects, CSV/plain text), or JSON whose structure defeats the configured flattener (e.g. usingField/path mismatches causing flatten exceptions).

Common situations: Kafka/Kinesis records that are not pure JSON (e.g. Avro-encoded or newline-delimited batches fed as single rows); empty strings from skipped queue messages; encoding issues corrupting payloads; wrong parser chosen for the data format.

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.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/991b80ab103845df. Report an issue: GitHub.