{"record":{"id":"077c540760c2e3ff","repo":"languagetool-org/languagetool","slug":"error-access-from-denied","errorCode":null,"errorMessage":"Error: Access from  denied","messagePattern":"Error: Access from  denied","errorType":"http","errorClass":"RuntimeException","httpStatus":403,"severity":"error","filePath":"languagetool-server/src/main/java/org/languagetool/server/LanguageToolHttpHandler.java","lineNumber":228,"sourceCode":"          String pathWithoutVersion = path.substring(\"/v2/\".length());\n          final Map<String, String> finalParameters = parameters;\n          final String finalRemoteAddress = remoteAddress;\n          TelemetryProvider.INSTANCE.createSpan(\"/v2\", Attributes.empty(), () -> apiV2.handleRequest(pathWithoutVersion, httpExchange, finalParameters, errorRequestLimiter, finalRemoteAddress, config));\n        } else if (path.endsWith(\"/Languages\")) {\n          throw new BadRequestException(\"You're using an old version of our API that's not supported anymore. Please see \" + API_DOC_URL);\n        } else if (path.equals(\"/\")) {\n          throw new BadRequestException(\"Missing arguments for LanguageTool API. Please see \" + API_DOC_URL);\n        } else if (path.contains(\"/v2/\")) {\n          throw new BadRequestException(\"You have '/v2/' in your path, but not at the root. Try an URL like 'http://server/v2/...' \");\n        } else if (path.equals(\"/favicon.ico\")) {\n          sendError(httpExchange, HttpURLConnection.HTTP_NOT_FOUND, \"Not found\");\n        } else {\n          throw new BadRequestException(\"This is the LanguageTool API. You have not specified any parameters. Please see \" + API_DOC_URL);\n        }\n      } else {\n        String errorMessage = \"Error: Access from \" + StringTools.escapeXML(origAddress) + \" denied\";\n        sendError(httpExchange, HttpURLConnection.HTTP_FORBIDDEN, errorMessage);\n        throw new RuntimeException(errorMessage);\n      }\n    } catch (Exception e) {\n      String response;\n      int errorCode;\n      boolean textLoggingAllowed = false;\n      boolean logStacktrace = true;\n      Throwable rootCause = ExceptionUtils.getRootCause(e);\n      if (e instanceof TextTooLongException || rootCause instanceof TextTooLongException) {\n        errorCode = HttpURLConnection.HTTP_ENTITY_TOO_LARGE;\n        response = e.getMessage();\n        logStacktrace = false;\n      } else if (e instanceof ErrorRateTooHighException || rootCause instanceof ErrorRateTooHighException) {\n        errorCode = HttpURLConnection.HTTP_BAD_REQUEST;\n        response = ExceptionUtils.getRootCause(e).getMessage();\n        logStacktrace = false;\n      } else if (hasCause(e, AuthException.class)) {\n        errorCode = HttpURLConnection.HTTP_FORBIDDEN;\n        response = AuthException.class.getName() + \": \" + e.getMessage();","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-server/src/main/java/org/languagetool/server/LanguageToolHttpHandler.java#L210-L246","documentation":"Thrown (as a RuntimeException and 403 HTTP response) when the client's IP address is not allowed by the server's access-control configuration. LanguageTool servers can restrict which remote addresses may connect; disallowed IPs are rejected before any API processing.","triggerScenarios":"Request originates from an IP not on the server's allowlist (configured via --allowedIps or the access-control config), so handle() takes the deny branch.","commonSituations":"Server started with an allowedIps restriction and a legitimate client connects from an unexpected address, connections through a load balancer/NAT whose forwarded IP isn't allowlisted, or Kubernetes/Docker networking exposing a different source IP.","solutions":["Restart the server including the client IP in the allowedIps list (or remove the restriction if acceptable)","If behind a proxy, ensure the real client IP is forwarded and matching the allowlist","Verify the client's egress IP and update firewall/proxy configuration accordingly"],"exampleFix":"// before\njava -cp languagetool-server.jar org.languagetool.server.HTTPServer --port 8081 --allowedIps 127.0.0.1\n// after\njava -cp languagetool-server.jar org.languagetool.server.HTTPServer --port 8081 --allowedIps 127.0.0.1,203.0.113.7","handlingStrategy":"try-catch","validationCode":"// confirm client egress IP against the server's allowedIps before calling\nconsole.log('Egress IP:', await fetch('https://api.ipify.org').then(r => r.text()));","typeGuard":null,"tryCatchPattern":"try {\n  const res = await fetch(url, opts);\n  if (res.status === 403) throw new Error(`IP not allowed by server: ${ip}`);\n} catch (e) {\n  // alert ops to update --allowedIps or proxy IP forwarding\n}","preventionTips":["Keep allowedIps in sync with all legitimate egress IPs (NAT, LB, k8s nodes)","Use X-Forwarded-For-aware setups when behind proxies","Document IP allowlist requirements for deployments"],"tags":["http","languagetool","access-control","forbidden"],"backgroundTag":"permission-denied","analyzedSha":"2e990059ce67d5e2a0f7f7ca5d31160c6709df4b","analyzedAt":"2026-09-06T09:20:17.015Z","contentChangedAt":"2026-09-06T09:20:17.015Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}