languagetool-org/languagetool · error · BadRequestException

'text' cannot be used with 'interpretAs' (only 'markup' can)

Error message

'text' cannot be used with 'interpretAs' (only 'markup' can): 

What it means

In the 'annotation' list of the /v2/check data parameter, 'interpretAs' is only allowed together with 'markup'. If an object has both 'text' and 'interpretAs', LanguageTool throws BadRequestException because there is no meaningful way to reinterpret explicit text.

Source

Thrown at languagetool-server/src/main/java/org/languagetool/server/ApiV2.java:486

    }
    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 {
    StringWriter sw = new StringWriter();
    try (JsonGenerator g = factory.createGenerator(sw)) {

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Remove 'interpretAs' from text objects.
  2. If reinterpretation is needed, convert the entry to {"markup":"...","interpretAs":"..."}.
  3. Validate the annotation JSON client-side before sending.

Example fix

// before
{"annotation":[{"text":"gr8","interpretAs":"great"}]}
// after
{"annotation":[{"markup":"gr8","interpretAs":"great"}]}
Defensive patterns

Strategy: validation

Validate before calling

for (const n of annotation) {
  if (n.text != null && n.interpretAs != null) throw new Error('interpretAs is only valid with markup: ' + JSON.stringify(n));
}

Type guard

function canUseInterpretAs(n) { return n.markup != null || n.interpretAs == 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('annotation payload invalid:', e.message);
}

Prevention

When it happens

Trigger: POSTing data like {"annotation":[{"text":"Hello","interpretAs":"Hi"}]} — a node with both non-null 'text' and 'interpretAs' keys (and no 'markup').

Common situations: Copying the markup+interpretAs pattern onto text objects; template-based payload builders that always emit interpretAs.

Related errors


AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06). Data as JSON: /api/errors/a66fe2dc67ead6c5. Report an issue: GitHub.