languagetool-org/languagetool · error · BadRequestException
<url> needs to be called with POST
Error message
<url> needs to be called with POST
What it means
ensurePostMethod guards mutating endpoints (/words/add, /words/delete, /users/refresh) that only accept POST. Any other HTTP method triggers a BadRequestException '<url> needs to be called with POST'.
Source
Thrown at languagetool-server/src/main/java/org/languagetool/server/ApiV2.java:408
StringWriter sw = new StringWriter();
new ObjectMapper().writeValue(sw, DatabaseAccess.getInstance().getExtendedUserInfo(user));
sendJson(httpExchange, sw);
}
} else {
throw new IllegalStateException("Could not fetch user information");
}
}
}
private void ensureGetMethod(HttpExchange httpExchange, String url) {
if (!httpExchange.getRequestMethod().equalsIgnoreCase("get")) {
throw new BadRequestException(url + " needs to be called with GET");
}
}
private void ensurePostMethod(HttpExchange httpExchange, String url) {
if (!httpExchange.getRequestMethod().equalsIgnoreCase("post")) {
throw new BadRequestException(url + " needs to be called with POST");
}
}
@NotNull
private UserLimits getUserLimits(Map<String, String> parameters, HTTPServerConfig config) {
UserLimits limits = ServerTools.getUserLimits(parameters, config);
if (limits.getPremiumUid() == null) {
throw new BadRequestException("This end point needs a user id");
}
return limits;
}
private void writeResponse(String fieldName, boolean added, HttpExchange httpExchange) throws IOException {
StringWriter sw = new StringWriter();
try (JsonGenerator g = factory.createGenerator(sw)) {
g.writeStartObject();
g.writeBooleanField(fieldName, added);
g.writeEndObject();View on GitHub (pinned to 2e990059ce)
Solutions
- Send the request as HTTP POST with parameters in the form-encoded body.
- Use POST (not the DELETE verb) for /v2/words/delete — the operation is encoded in the URL, not the method.
- Do not call these endpoints via browser address bar or simple GET links; use a client that can POST.
Example fix
// before curl -X DELETE 'https://server/v2/words/delete?username=u&token=t&word=foo' // after curl -X POST 'https://server/v2/words/delete' -d 'username=u&token=t&word=foo'
Defensive patterns
Strategy: validation
Validate before calling
if (method.toUpperCase() !== 'POST') throw new Error(`${url} must be called with POST; put params in the form body`); Prevention
- Use POST (the verb) for /words/add, /words/delete and /users/refresh regardless of path names.
- Send parameters as an application/x-www-form-urlencoded POST body.
- Do not access mutating endpoints via GET links or the browser address bar.
When it happens
Trigger: GET to /v2/words/add or /v2/words/delete; DELETE HTTP verb used against /words/delete; link-based or browser navigation hitting the endpoint with GET.
Common situations: Clients confusing the word-removal endpoint path ('/words/delete') with the DELETE HTTP method; test harnesses defaulting to GET; firewalls or tools rewriting POST bodies and falling back to GET.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- <url> needs to be called with GET
- Missing 'text' or 'data' parameter
- Use parameter 'dicts', not 'dict' in GET /words API method.
- 'lang' parameter missing
- 'ruleId' parameter missing
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/42d1503e769848a5.
Report an issue: GitHub.