{"record":{"id":"6453b2c0a2458d52","repo":"languagetool-org/languagetool","slug":"no-data-found-in-grammalecte-json-map","errorCode":null,"errorMessage":"No 'data' found in grammalecte JSON: \" + map","messagePattern":"No 'data' found in grammalecte JSON: \" \\+ map","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"languagetool-language-modules/fr/src/main/java/org/languagetool/rules/fr/GrammalecteRule.java","lineNumber":653,"sourceCode":"      List<RuleMatch> ruleMatches = parseJson(input);\n      return toRuleMatchArray(ruleMatches);\n    } catch (Exception e) {\n      lastRequestError = System.currentTimeMillis();\n      // These are issue that can be request-specific, like wrong parameters. We don't throw an\n      // exception, as the calling code would otherwise assume this is a persistent error:\n      logger.warn(\"Warn: Failed to query Grammalecte server at \" + serverUrl, e);\n    } finally {\n      huc.disconnect();\n    }\n    return RuleMatch.EMPTY_ARRAY;\n  }\n\n  @NotNull\n  private List<RuleMatch> parseJson(InputStream inputStream) throws IOException {\n    Map map = mapper.readValue(inputStream, Map.class);\n    List matches = (ArrayList) map.get(\"data\");\n    if (matches == null) {\n      throw new RuntimeException(\"No 'data' found in grammalecte JSON: \" + map);  // handled in match()\n    }\n    List<RuleMatch> result = new ArrayList<>();\n    for (Object match : matches) {\n      List<RuleMatch> remoteMatches = getMatches((Map<String, Object>)match);\n      result.addAll(remoteMatches);\n    }\n    return result;\n  }\n\n  protected String encode(String plainText) throws UnsupportedEncodingException {\n    return URLEncoder.encode(plainText, StandardCharsets.UTF_8.name());\n  }\n\n  @NotNull\n  private List<RuleMatch> getMatches(Map<String, Object> match) {\n    List<RuleMatch> remoteMatches = new ArrayList<>();\n    ArrayList matches = (ArrayList) match.get(\"lGrammarErrors\");\n    for (Object o : matches) {","sourceCodeStart":635,"sourceCodeEnd":671,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-language-modules/fr/src/main/java/org/languagetool/rules/fr/GrammalecteRule.java#L635-L671","documentation":"GrammalecteRule.parseJson deserializes the Grammalecte server's response and requires a top-level 'data' array holding the rule matches. When the key is absent (matches == null), it throws this RuntimeException; the surrounding match() is expected to handle it, since a response without 'data' means the server replied in an unexpected shape (often an error page or a different API version).","triggerScenarios":"ruleMatches calls parseJson with an InputStream whose parsed JSON map has no 'data' entry — e.g. the Grammalecte server returned an error payload, a non-grammalecte response (proxy/captive portal), or an incompatible Grammalecte version with a changed response schema.","commonSituations":"Grammalecte server down and something upstream answers with an HTML error page that happens to parse as JSON, wrong grammalecte server URL/port configured, version mismatch between LanguageTool and the Grammalecte server API.","solutions":["Check that the configured Grammalecte server URL is correct and reachable (curl it and inspect the raw JSON).","Upgrade/downgrade the Grammalecte server to a version whose response contains the 'data' array.","Ensure no proxy or error page is intercepting the request; verify response content-type is application/json from Grammalecte.","The comment says it's handled in match() — confirm your caller catches this and reports a degraded result instead of failing the session."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// before trusting the response\nMap map = mapper.readValue(inputStream, Map.class);\nif (map == null || !map.containsKey(\"data\")) {\n    LOG.warn(\"Grammalecte response missing 'data': \" + map);\n    return Collections.emptyList();\n}","typeGuard":null,"tryCatchPattern":"try { matches = rule.ruleMatches(text, sentence); } catch (RuntimeException e) { LOG.warn(\"Grammalecte unavailable: \" + e.getMessage()); matches = Collections.emptyList(); }","preventionTips":["Pin a Grammalecte server version compatible with LanguageTool's expected schema.","Health-check the Grammalecte endpoint before running checks.","Bypass proxies/interceptors that can replace the JSON body with error pages."],"tags":["java","languagetool","json","remote-rules","french"],"backgroundTag":"unexpected-response-shape","analyzedSha":"2e990059ce67d5e2a0f7f7ca5d31160c6709df4b","analyzedAt":"2026-09-06T09:20:17.015Z","contentChangedAt":"2026-09-06T09:20:17.015Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}