apache/druid · warning · ParseException

Unable to parse row [%s]

Error message

Unable to parse row [%s]

What it means

JsonReader.parseInputRows catches Jackson's JsonParseException bubbling out of MappingIterator and rethrows it as a Druid ParseException ('Unable to parse row [ ... ]') containing the raw input text, converting Jackson failures into Druid's skippable-record mechanism. Unknown runtime exceptions are rethrown unchanged.

Source

Thrown at processing/src/main/java/org/apache/druid/data/input/impl/JsonReader.java:135

  }

  @Override
  protected List<InputRow> parseInputRows(InputEntity entity) throws IOException, ParseException
  {
    final List<InputRow> inputRows = new ArrayList<>();
    try (JsonParser parser = jsonFactory.createParser(entity.open())) {
      final MappingIterator<JsonNode> delegate = mapper.readValues(parser, JsonNode.class);
      while (delegate.hasNext()) {
        final JsonNode row = delegate.next();
        inputRows.add(MapInputRowParser.parse(inputRowSchema, flattener.flatten(row)));
      }
    }
    catch (RuntimeException e) {
      //convert Jackson's JsonParseException into druid's exception for further processing
      //JsonParseException will be thrown from MappingIterator#hasNext or MappingIterator#next when input json text is ill-formed
      if (e.getCause() instanceof JsonParseException) {
        final String rowAsString = IOUtils.toString(entity.open(), StandardCharsets.UTF_8);
        throw new ParseException(rowAsString, e, "Unable to parse row [%s]", rowAsString);
      }

      //throw unknown exception
      throw e;
    }

    if (inputRows.isEmpty()) {
      final String rowAsString = IOUtils.toString(entity.open(), StandardCharsets.UTF_8);
      throw new ParseException(
          rowAsString,
          "Unable to parse [%s] as the intermediateRow resulted in empty input row",
          rowAsString
      );
    }

    return inputRows;
  }

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Inspect the raw row echoed in the ParseException message for the syntax error
  2. For newline-delimited JSON, set "assumeNewlineDelimited": true (or use a newline-delimited input format) instead of feeding an array/root document
  3. Repair or re-export the corrupted/truncated source file
  4. Let Druid skip the bad record if the task's parseException policy tolerates it

Example fix

// before: NDJSON file read as one JSON doc
{"type":"json","findColumnsFromHeader":false}
// after
{"type":"json","assumeNewlineDelimited":true}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate JSON text is parseable before feeding the reader
try (JsonParser p = objectMapper.getFactory().createParser(rawBytes)) { while (p.nextToken() != null) {} }

Try / catch

try { rows = reader.read().collect(toList()); } catch (ParseException e) { log.warn("Bad row skipped: {}", e.getMessage()); } catch (RuntimeException e) { throw e; /* non-JSON errors are rethrown by design */ }

Prevention

When it happens

Trigger: Ill-formed JSON text in the input entity — truncated lines, concatenated objects without newlineDelimited, stray characters, invalid escapes — causing MappingIterator#hasNext/next to throw JsonParseException.

Common situations: NDJSON ingested without assumeNewlineDelimited/line-input-format so the parser hits the second object prematurely; truncated files from failed uploads; JSON with trailing commas or single quotes from non-standard producers.

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/01661792541d457a. Report an issue: GitHub.