languagetool-org/languagetool · error · BadRequestException

This end point needs a user id

Error message

This end point needs a user id

What it means

LanguageTool's /v2 API endpoint requires a premium user id to resolve account limits. getUserLimits delegates to ServerTools.getUserLimits and, if the resulting UserLimits has no premium UID (no 'username' parameter and/or no configured premium server mapping), it throws BadRequestException. This means the endpoint was called anonymously or with credentials that did not resolve to a premium user.

Source

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

  }

  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();
    }
    sendJson(httpExchange, sw);
  }
  
  private void writeListResponse(String fieldName, List<String> words, HttpExchange httpExchange) throws IOException {
    StringWriter sw = new StringWriter();
    try (JsonGenerator g = factory.createGenerator(sw)) {
      g.writeStartObject();

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Add the 'username' (and 'apiKey' if required) parameters to the request.
  2. Verify the account is a premium account registered on the server.
  3. If self-hosting, ensure HTTPServerConfig premium settings are set so usernames resolve to a premium UID.
  4. Check the endpoint actually requires user limits; use an endpoint that supports anonymous access otherwise.

Example fix

// before
curl 'https://api.languagetool.org/v2/limits'
// after
curl -d 'username=myuser&apiKey=MYKEY' 'https://api.languagetool.org/v2/limits'
Defensive patterns

Strategy: validation

Validate before calling

if (!params.has('username') || !params.get('username')) {
  throw new Error('This endpoint requires a premium username parameter');
}

Type guard

function hasPremiumUser(params) {
  return typeof params.username === 'string' && params.username.trim().length > 0;
}

Try / catch

try {
  const res = await fetch(url, { method: 'POST', body: form });
  if (res.status === 400) throw new Error(await res.text());
} catch (e) {
  console.error('User limits request rejected:', e.message);
}

Prevention

When it happens

Trigger: Calling a limits-related API v2 endpoint without a 'username' parameter, with an empty username, or with a username that does not map to a premium UID given the server's HTTPServerConfig.

Common situations: Scripts testing the public API without an account; self-hosted servers hit with endpoints that assume premium setup; clients that dropped the username field after a parameter rename.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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