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
- Request the endpoint with /v2 at the path root, e.g. http://server/v2/check
- Configure the reverse proxy to strip the outer base path before forwarding to LanguageTool
- 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
- Strip deployment base paths in the reverse proxy before forwarding
- Never hardcode a context root into the LanguageTool endpoint URL
- Test proxy configs with curl against a known-good /v2/check call
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
- Missing 'text' or 'data' parameter
- Use parameter 'dicts', not 'dict' in GET /words API method.
- 'lang' parameter missing
- 'ruleId' parameter missing
- Rule '<ruleId>' not found for language <lang> (LanguageTool
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/631760a97ac4d351.
Report an issue: GitHub.