languagetool-org/languagetool · error

Failed to parse line

Error message

Failed to parse line 

What it means

LightRuleMatchParser.parseAggregatedJson wraps any exception raised while parsing a line of the aggregated JSON input file into a RuntimeException. The message includes the line number (lineCount) and the input file path so the offending JSON line can be located; the original exception is attached as the cause (e.g. Jackson mapping or NPE errors).

Source

Thrown at languagetool-dev/src/main/java/org/languagetool/dev/diff/LightRuleMatchParser.java:72

    ObjectMapper mapper = new ObjectMapper();
    List<LightRuleMatch> ruleMatches = new ArrayList<>();
    Set<String> buildDates = new HashSet<>();
    int lineCount = 1;
    try (Scanner scanner = new Scanner(inputFile)) {
      while (scanner.hasNextLine()) {
        String line = scanner.nextLine();
        JsonNode node = mapper.readTree(line);
        JsonNode matches = node.get("matches");
        JsonNode software = node.get("software");
        String buildDate = software != null ? software.get("buildDate").asText() : "unknown";
        buildDates.add(buildDate);
        for (JsonNode match : matches) {
          ruleMatches.add(nodeToLightMatch(node.get("title").asText(), match));
        }
        lineCount++;
      }
    } catch (Exception e) {
      throw new RuntimeException("Failed to parse line " + lineCount + " of " + inputFile, e);
    }
    return new JsonParseResult(ruleMatches, buildDates);
  }

  @NotNull
  private LightRuleMatch nodeToLightMatch(String title, JsonNode match) {
    int offset = match.get("offset").asInt();
    JsonNode rule = match.get("rule");
    String ruleId = rule.get("id").asText();
    String fullRuleId = rule.get("subId") != null ? ruleId + "[" + rule.get("subId").asText() + "]" : ruleId;
    String message = match.get("message").asText();
    String category = rule.get("category") != null ? rule.get("category").get("name").asText() : "(unknown)";
    //TODO: Maybe just works for xml rules
    boolean isPremium = rule.get("isPremium") != null && rule.get("isPremium").asBoolean(false);
    int contextOffset = match.get("context").get("offset").asInt();
    int contextLength = match.get("context").get("length").asInt();
    String context = match.get("context").get("text").asText();
    int maxEnd = Math.min(contextOffset + contextLength, context.length());

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Open the input file at the reported line number and fix or remove the malformed JSON line
  2. Regenerate the aggregated input file with the same LanguageTool version the parser expects
  3. Validate each line is complete JSON (e.g. with jq) before feeding it to the parser
  4. Inspect the cause chain (e.getCause()) to see whether it is a JSON syntax error or a missing-field NPE

Example fix

// before: feeding possibly blank/truncated lines
result = parser.parseOutput(new File("agg.jsonl"));
// after: pre-validate lines
for (String line : Files.readAllLines(Path.of("agg.jsonl"))) {
  if (line.isBlank()) continue;
  new JsonParser().parse(line); // throws early with clear context
}
result = parser.parseOutput(new File("agg.jsonl"));
Defensive patterns

Strategy: validation

Validate before calling

// validate each JSONL line before parsing
for (String line : Files.readAllLines(Path.of(inputFile))) {
  if (line.isBlank()) throw new IllegalStateException("blank line in " + inputFile);
  JsonNode n = new ObjectMapper().readTree(line);
  if (!n.has("matches")) throw new IllegalStateException("line missing 'matches': " + line);
}

Type guard

static boolean isValidAggLine(String line) {
  try { return new ObjectMapper().readTree(line).has("matches"); }
  catch (Exception e) { return false; }
}

Try / catch

try {
  result = parser.parseOutput(inputFile);
} catch (RuntimeException e) {
  // message contains 'Failed to parse line N of FILE'
  log.error("aggregated JSON invalid: " + e.getMessage(), e.getCause());
  throw new IllegalArgumentException("fix input file at reported line", e);
}

Prevention

When it happens

Trigger: Calling parseOutput/parseAggregatedJson on an aggregated JSON file where a line is malformed JSON, is missing expected fields (e.g. 'matches', rule match 'title' node), or has an unexpected structure so nodeToLightMatch throws.

Common situations: Feed files produced by an older LanguageTool version no longer matching the expected aggregated JSON schema; hand-edited or truncated diff input files; running the diff tooling against output from a differently configured server.

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 languagetool-org/languagetool@2e990059ce (2026-09-06). Data as JSON: /api/errors/46e79e3b43e15463. Report an issue: GitHub.