{"record":{"id":"277bbf700e61f942","repo":"languagetool-org/languagetool","slug":"could-not-parse-json-from-data-parameter","errorCode":null,"errorMessage":"Could not parse JSON from 'data' parameter","messagePattern":"Could not parse JSON from 'data' parameter","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"languagetool-server/src/main/java/org/languagetool/server/ApiV2.java","lineNumber":157,"sourceCode":"    ServerTools.setCommonHeaders(httpExchange, JSON_CONTENT_TYPE, allowOriginUrl);\n    httpExchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.getBytes(ENCODING).length);\n    httpExchange.getResponseBody().write(response.getBytes(ENCODING));\n    ServerMetricsCollector.getInstance().logResponse(HttpURLConnection.HTTP_OK);\n  }\n\n  private void handleCheckRequest(HttpExchange httpExchange, Map<String, String> parameters, ErrorRequestLimiter errorRequestLimiter, String remoteAddress, HTTPServerConfig config) throws Exception {\n    AnnotatedText aText;\n    if (parameters.containsKey(\"text\") && parameters.containsKey(\"data\")) {\n      throw new BadRequestException(\"Set only 'text' or 'data' parameter, not both\");\n    } else if (parameters.containsKey(\"text\")) {\n      aText = new AnnotatedTextBuilder().addText(parameters.get(\"text\")).build();\n    } else if (parameters.containsKey(\"data\")) {\n      ObjectMapper mapper = new ObjectMapper();\n      JsonNode data;\n      try {\n        data = mapper.readTree(parameters.get(\"data\"));\n      } catch (JsonProcessingException e) {\n        throw new BadRequestException(\"Could not parse JSON from 'data' parameter\", e);\n      }\n      if (data.get(\"text\") != null && data.get(\"annotation\") != null) {\n        throw new BadRequestException(\"'data' key in JSON requires either 'text' or 'annotation' key, not both\");\n      } else if (data.get(\"text\") != null) {\n        aText = getAnnotatedTextFromString(data, data.get(\"text\").asText());\n      } else if (data.get(\"annotation\") != null) {\n        aText = getAnnotatedTextFromJson(data);\n      } else {\n        throw new BadRequestException(\"'data' key in JSON requires 'text' or 'annotation' key\");\n      }\n    } else {\n      throw new BadRequestException(\"Missing 'text' or 'data' parameter\");\n    }\n    //get from config\n    if (config.logIp && aText.getPlainText().trim().equals(config.logIpMatchingPattern)) {\n      handleIpLogMatch(httpExchange, remoteAddress, parameters);\n      //no need to check text again rules\n      return;","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-server/src/main/java/org/languagetool/server/ApiV2.java#L139-L175","documentation":"When the 'data' parameter is supplied to /v2/check, the server parses it as JSON with Jackson. If the value is not valid JSON (JsonProcessingException), it throws BadRequestException with this message and the parse error as cause.","triggerScenarios":"POST /v2/check with data=... containing malformed JSON: unquoted strings, trailing commas, single quotes, HTML-encoding artifacts, or truncated payloads.","commonSituations":"Manually built JSON without escaping quotes/newlines; clients sending URL-encoded JSON that got double-encoded; frameworks munging the raw body.","solutions":["Validate the JSON with a parser before sending it","Serialize the data object with a JSON library instead of string concatenation","Send it correctly URL-encoded (e.g. curl --data-urlencode)","Check the cause JsonProcessingException for the exact position of the syntax error"],"exampleFix":"// before\ncurl -d 'data={text: \"Hello\"}' --data-urlencode 'language=en-US' http://server:8081/v2/check\n// after\ncurl --data-urlencode 'data={\"text\":\"Hello\"}' --data-urlencode 'language=en-US' http://server:8081/v2/check","handlingStrategy":"validation","validationCode":"let parsed;\ntry { parsed = JSON.parse(dataPayload); }\ncatch (e) { throw new Error('data must be valid JSON: ' + e.message); }","typeGuard":"function isValidJson(s) { try { JSON.parse(s); return true; } catch { return false; } }","tryCatchPattern":"try {\n    const res = await check({ data: jsonPayload });\n} catch (e) {\n    if (/Could not parse JSON/.test(e.message)) {\n        console.error('Fix JSON syntax in data parameter');\n    } else throw e;\n}","preventionTips":["Serialize payloads with a JSON library, never string concatenation","URL-encode the data parameter (curl --data-urlencode)","Round-trip JSON.parse(JSON.stringify(payload)) as a sanity check before sending"],"tags":["http","api","json","languagetool-server"],"backgroundTag":"json-parse-error","analyzedSha":"2e990059ce67d5e2a0f7f7ca5d31160c6709df4b","analyzedAt":"2026-09-06T09:20:17.015Z","contentChangedAt":"2026-09-06T09:20:17.015Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}