{"record":{"id":"f1a99b7ca4f29015","repo":"languagetool-org/languagetool","slug":"set-only-text-or-data-parameter-not-both","errorCode":null,"errorMessage":"Set only 'text' or 'data' parameter, not both","messagePattern":"Set only 'text' or 'data' parameter, not both","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"languagetool-server/src/main/java/org/languagetool/server/ApiV2.java","lineNumber":148,"sourceCode":"    String response = getConfigurationInfo(lang, config);\n    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 handleSoftwareInfoRequest(HttpExchange httpExchange) throws IOException {\n    String response = getSoftwareInfo();\n    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\");","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-server/src/main/java/org/languagetool/server/ApiV2.java#L130-L166","documentation":"/v2/check accepts the input either as plain 'text' or as structured 'data' (JSON annotation), never both, because the source of the annotated text would be ambiguous. Sending both throws BadRequestException (HTTP 400).","triggerScenarios":"POST /v2/check with both text=... and data=... in the same request body or query string.","commonSituations":"Client code adding a default text parameter while also forwarding a JSON data payload; proxy layers merging parameters.","solutions":["Remove either the 'text' or the 'data' parameter so only one remains","Send plain text via 'text' or annotated content via 'data', not both","Review client code for parameter defaults that inject 'text' alongside 'data'"],"exampleFix":"// before\ncurl -d 'text=Hello' -d 'data={\"text\":\"Hello\"}' --data-urlencode 'language=en-US' http://server:8081/v2/check\n// after\ncurl -d 'text=Hello' --data-urlencode 'language=en-US' http://server:8081/v2/check","handlingStrategy":"validation","validationCode":"const keys = ['text', 'data'].filter(k => k in payload);\nif (keys.length === 2) throw new Error(\"Send only 'text' or 'data', not both\");","typeGuard":null,"tryCatchPattern":"try {\n    const res = await check(params);\n} catch (e) {\n    if (/Set only 'text' or 'data'/.test(e.message)) {\n        console.error('Remove the duplicate input parameter');\n    } else throw e;\n}","preventionTips":["Pick one input mode per request: plain text OR annotated data","Audit client code for default text parameters merged into data requests","Write an integration test asserting exactly one input parameter is sent"],"tags":["http","api","validation","languagetool-server"],"backgroundTag":"mutually-exclusive-flags","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"}