languagetool-org/languagetool · error · BadRequestException
Could not parse JSON from 'data' parameter
Error message
Could not parse JSON from 'data' parameter
What it means
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.
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
Example fix
// before
curl -d 'data={text: "Hello"}' --data-urlencode 'language=en-US' http://server:8081/v2/check
// after
curl --data-urlencode 'data={"text":"Hello"}' --data-urlencode 'language=en-US' http://server:8081/v2/check Defensive patterns
Strategy: validation
Validate before calling
let parsed;
try { parsed = JSON.parse(dataPayload); }
catch (e) { throw new Error('data must be valid JSON: ' + e.message); } Type guard
function isValidJson(s) { try { JSON.parse(s); return true; } catch { return false; } } Try / catch
try {
const res = await check({ data: jsonPayload });
} catch (e) {
if (/Could not parse JSON/.test(e.message)) {
console.error('Fix JSON syntax in data parameter');
} else throw e;
} Prevention
- 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
When it happens
Trigger: POST /v2/check with data=... containing malformed JSON: unquoted strings, trailing commas, single quotes, HTML-encoding artifacts, or truncated payloads.
Common situations: Manually built JSON without escaping quotes/newlines; clients sending URL-encoded JSON that got double-encoded; frameworks munging the raw body.
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
- 'data' key in JSON requires either 'text' or 'annotation'…
- 'data' key in JSON requires 'text' or 'annotation' key
- 'language' parameter missing
- Only either 'text' or 'markup' are supported in an object…
- Only 'text' and 'markup' are supported in 'annotation'…
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/277bbf700e61f942.
Report an issue: GitHub.
Appendix: source
Thrown at languagetool-server/src/main/java/org/languagetool/server/ApiV2.java:157
ServerTools.setCommonHeaders(httpExchange, JSON_CONTENT_TYPE, allowOriginUrl);
httpExchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.getBytes(ENCODING).length);
httpExchange.getResponseBody().write(response.getBytes(ENCODING));
ServerMetricsCollector.getInstance().logResponse(HttpURLConnection.HTTP_OK);
}
private void handleCheckRequest(HttpExchange httpExchange, Map<String, String> parameters, ErrorRequestLimiter errorRequestLimiter, String remoteAddress, HTTPServerConfig config) throws Exception {
AnnotatedText aText;
if (parameters.containsKey("text") && parameters.containsKey("data")) {
throw new BadRequestException("Set only 'text' or 'data' parameter, not both");
} else if (parameters.containsKey("text")) {
aText = new AnnotatedTextBuilder().addText(parameters.get("text")).build();
} else if (parameters.containsKey("data")) {
ObjectMapper mapper = new ObjectMapper();
JsonNode data;
try {
data = mapper.readTree(parameters.get("data"));
} catch (JsonProcessingException e) {
throw new BadRequestException("Could not parse JSON from 'data' parameter", e);
}
if (data.get("text") != null && data.get("annotation") != null) {
throw new BadRequestException("'data' key in JSON requires either 'text' or 'annotation' key, not both");
} else if (data.get("text") != null) {
aText = getAnnotatedTextFromString(data, data.get("text").asText());
} else if (data.get("annotation") != null) {
aText = getAnnotatedTextFromJson(data);
} else {
throw new BadRequestException("'data' key in JSON requires 'text' or 'annotation' key");
}
} else {
throw new BadRequestException("Missing 'text' or 'data' parameter");
}
//get from config
if (config.logIp && aText.getPlainText().trim().equals(config.logIpMatchingPattern)) {
handleIpLogMatch(httpExchange, remoteAddress, parameters);
//no need to check text again rules
return;View on GitHub (pinned to 2e990059ce)