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

  1. Send the request as HTTP POST with parameters in the form-encoded body.
  2. Use POST (not the DELETE verb) for /v2/words/delete — the operation is encoded in the URL, not the method.
  3. 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

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


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