languagetool-org/languagetool · error · BadRequestException
Only 'text' and 'markup' are supported in 'annotation' list:
Error message
Only 'text' and 'markup' are supported in 'annotation' list:
What it means
Each object in the 'annotation' list must have a non-null 'text' or 'markup' key. If a node has neither (e.g. only 'interpretAs' or an unrelated/empty object), LanguageTool throws BadRequestException since it cannot interpret the entry.
Source
Thrown at languagetool-server/src/main/java/org/languagetool/server/ApiV2.java:496
// {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 {
StringWriter sw = new StringWriter();
try (JsonGenerator g = factory.createGenerator(sw)) {
g.writeStartArray();
List<Language> languages = new ArrayList<>(Languages.get());
Set<String> longCodes = new HashSet<>();
for (Language lang : languages) {
g.writeStartObject();
g.writeStringField("name", lang.getName());
g.writeStringField("code", lang.getShortCode());
g.writeStringField("longCode", lang.getShortCodeWithCountryAndVariant());
longCodes.add(lang.getShortCodeWithCountryAndVariant());
g.writeEndObject();View on GitHub (pinned to 2e990059ce)
Solutions
- Ensure every annotation object has exactly one of 'text' or 'markup'.
- Remove empty or malformed objects from the list.
- Inspect the offending node echoed in the message to find the malformed entry.
Example fix
// before
{"annotation":[{"interpretAs":"x"}]}
// after
{"annotation":[{"text":"x"}]} Defensive patterns
Strategy: validation
Validate before calling
annotation.forEach((n, i) => {
if (n.text == null && n.markup == null) throw new Error(`annotation[${i}] needs 'text' or 'markup'`);
}); Type guard
function isValidAnnotationNode(n) {
return n != null && (n.text != null || n.markup != null);
} Try / catch
try {
const res = await fetch('/v2/check', { method: 'POST', body: form });
if (res.status === 400) throw new Error(await res.text());
} catch (e) {
console.error('invalid annotation entry:', e.message);
} Prevention
- Filter out empty objects before sending annotation lists.
- Ensure serializers do not omit empty-string text/markup keys (use '' not omission).
- Type annotation entries in your client code.
When it happens
Trigger: POSTing data with annotation objects like {} or {"interpretAs":"foo"} or {"other":"x"} to /v2/check.
Common situations: Serialization bugs that drop the 'text'/'markup' key when its value is empty; schema drift between client and API versions.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Only either 'text' or 'markup' are supported in an object in
- 'text' cannot be used with 'interpretAs' (only 'markup' can)
- '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/7fb519ca4ca6c2be.
Report an issue: GitHub.