languagetool-org/languagetool · error · BadRequestException
Only either 'text' or 'markup' are supported in an object in
Error message
Only either 'text' or 'markup' are supported in an object in 'annotation' list, not both: <node>
What it means
When building AnnotatedText from the JSON 'annotation' list, each object may specify 'text' or 'markup', not both. LanguageTool throws BadRequestException when a single annotation object contains both keys, because it cannot decide whether the content is checkable text or non-checked markup.
Source
Thrown at languagetool-server/src/main/java/org/languagetool/server/ApiV2.java:484
}
}
}
return textBuilder.build();
}
private AnnotatedText getAnnotatedTextFromJson(JsonNode data) {
AnnotatedTextBuilder atb = new AnnotatedTextBuilder();
// Expected format:
// annotation: [
// {text: 'text'},
// {markup: '<b>'}
// {text: 'more text'},
// {markup: '</b>'}
// ]
//
for (JsonNode node : data.get("annotation")) {
if (node.get("text") != null && node.get("markup") != null) {
throw new BadRequestException("Only either 'text' or 'markup' are supported in an object in 'annotation' list, not both: " + node);
} else if (node.get("text") != null && node.get("interpretAs") != null) {
throw new BadRequestException("'text' cannot be used with 'interpretAs' (only 'markup' can): " + node);
} else if (node.get("text") != null) {
atb.addText(node.get("text").asText());
} else if (node.get("markup") != null) {
if (node.get("interpretAs") != null) {
atb.addMarkup(node.get("markup").asText(), node.get("interpretAs").asText());
} else {
atb.addMarkup(node.get("markup").asText());
}
} else {
throw new BadRequestException("Only 'text' and 'markup' are supported in 'annotation' list: " + node);
}
}
return atb.build();
}
String getLanguages() throws IOException {View on GitHub (pinned to 2e990059ce)
Solutions
- Split the object into two entries: one {"markup":...} followed by one {"text":...}.
- Remove the 'markup' key if the content should be checked as text.
- Remove the 'text' key if the content is markup; use 'interpretAs' with markup if it should be checked as text.
Example fix
// before
{"annotation":[{"text":"Hello","markup":"<b>"}]}
// after
{"annotation":[{"markup":"<b>"},{"text":"Hello"}]} Defensive patterns
Strategy: validation
Validate before calling
for (const n of annotation) {
if ('text' in n && 'markup' in n) throw new Error('Annotation object must not have both text and markup: ' + JSON.stringify(n));
} Type guard
function isTextOnly(n) { return n.text != null && n.markup == null && n.interpretAs == null; }
function isMarkupOnly(n) { return n.markup != null && n.text == null; } Try / catch
try {
const res = await fetch('/v2/check', { method: 'POST', body: form });
if (!res.ok) throw new Error((await res.json()).message);
} catch (e) {
console.error('annotation payload invalid:', e.message);
} Prevention
- Build annotation objects with a single discriminated key per entry.
- Validate the annotation JSON schema client-side before sending.
- Split mixed content into consecutive markup/text entries.
When it happens
Trigger: POSTing to /v2/check with data JSON like {"annotation":[{"text":"Hello","markup":"<b>"}]} — any annotation object having both a non-null 'text' and a non-null 'markup' key.
Common situations: Clients programmatically generating the annotation list that accidentally include markup alongside text; hand-written payloads where markup was pasted into a text object.
Related errors
- 'text' cannot be used with 'interpretAs' (only 'markup' can)
- Only 'text' and 'markup' are supported in 'annotation' list:
- 'data' key in JSON requires either 'text' or 'annotation' ke
- 'data' key in JSON requires 'text' or 'annotation' key
- Invalid format for 'preferredVariants', expected a dash as i
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/43aaa83debfded35.
Report an issue: GitHub.