languagetool-org/languagetool · warning · BadRequestException

You have '/v2/' in your path, but not at the root. Try an UR

Error message

You have '/v2/' in your path, but not at the root. Try an URL like 'http://server/v2/...' 

What it means

Thrown when the request path contains '/v2/' but not at the start (root) of the path, e.g. '/api/v2/check'. The server only recognizes the version prefix when it directly follows the host, so embedded version segments are treated as unknown paths.

Source

Thrown at languagetool-server/src/main/java/org/languagetool/server/LanguageToolHttpHandler.java:219

        return;
      }
      if (workQueueFull(httpExchange, parameters, "Error: There are currently too many parallel requests. Please try again later.")) {
        ServerMetricsCollector.getInstance().logRequestError(ServerMetricsCollector.RequestErrorType.QUEUE_FULL);
        return;
      }
      if (allowedIps == null || allowedIps.contains(origAddress)) {
        if (path.startsWith("/v2/")) {
          ApiV2 apiV2 = new ApiV2(textCheckerV2, config.getAllowOriginUrl());
          String pathWithoutVersion = path.substring("/v2/".length());
          final Map<String, String> finalParameters = parameters;
          final String finalRemoteAddress = remoteAddress;
          TelemetryProvider.INSTANCE.createSpan("/v2", Attributes.empty(), () -> apiV2.handleRequest(pathWithoutVersion, httpExchange, finalParameters, errorRequestLimiter, finalRemoteAddress, config));
        } else if (path.endsWith("/Languages")) {
          throw new BadRequestException("You're using an old version of our API that's not supported anymore. Please see " + API_DOC_URL);
        } else if (path.equals("/")) {
          throw new BadRequestException("Missing arguments for LanguageTool API. Please see " + API_DOC_URL);
        } else if (path.contains("/v2/")) {
          throw new BadRequestException("You have '/v2/' in your path, but not at the root. Try an URL like 'http://server/v2/...' ");
        } else if (path.equals("/favicon.ico")) {
          sendError(httpExchange, HttpURLConnection.HTTP_NOT_FOUND, "Not found");
        } else {
          throw new BadRequestException("This is the LanguageTool API. You have not specified any parameters. Please see " + API_DOC_URL);
        }
      } else {
        String errorMessage = "Error: Access from " + StringTools.escapeXML(origAddress) + " denied";
        sendError(httpExchange, HttpURLConnection.HTTP_FORBIDDEN, errorMessage);
        throw new RuntimeException(errorMessage);
      }
    } catch (Exception e) {
      String response;
      int errorCode;
      boolean textLoggingAllowed = false;
      boolean logStacktrace = true;
      Throwable rootCause = ExceptionUtils.getRootCause(e);
      if (e instanceof TextTooLongException || rootCause instanceof TextTooLongException) {
        errorCode = HttpURLConnection.HTTP_ENTITY_TOO_LARGE;

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Request the endpoint with /v2 at the path root, e.g. http://server/v2/check
  2. Configure the reverse proxy to strip the outer base path before forwarding to LanguageTool
  3. If a base path is required, use a proxy rewrite rule (e.g. nginx proxy_pass http://backend/v2/check)

Example fix

// before
curl http://myhost/languagetool/v2/check?text=hi
// after
curl -X POST http://myhost/v2/check -d 'text=hi&language=en-US'
Defensive patterns

Strategy: validation

Validate before calling

const p = new URL(endpoint).pathname;
if (!/^\/v2(\/|$)/.test(p)) throw new Error(`'/v2/' must be at path root, got '${p}'`);

Prevention

When it happens

Trigger: Requests where '/v2/' appears mid-path, such as mounting LanguageTool under a base path like '/languagetool/v2/check' without rewriting the URL before it reaches the server.

Common situations: Reverse-proxy setups that preserve a context root instead of stripping it, Docker/nginx configs forwarding the full original path, or clients hardcoding a deployment base path into the endpoint URL.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


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