{"record":{"id":"bf38a474e3e690ed","repo":"apple/pkl","slug":"rewrite-rule-must-end-with-but-was-s","errorCode":null,"errorMessage":"Rewrite rule must end with '/', but was '%s'","messagePattern":"Rewrite rule must end with '/', but was '(.+?)'","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pkl-core/src/main/java/org/pkl/core/util/IoUtils.java","lineNumber":905,"sourceCode":"    }\n    return index;\n  }\n\n  public static void validateFileUri(URI uri) throws URISyntaxException {\n    if (!uri.getSchemeSpecificPart().startsWith(\"/\")) {\n      throw new URISyntaxException(uri.toString(), ErrorMessages.create(\"invalidOpaqueFileUri\"));\n    }\n  }\n\n  public static void validateRewriteRule(URI rewrite) {\n    if (!Objects.equals(rewrite.getScheme(), \"http\")\n        && !Objects.equals(rewrite.getScheme(), \"https\")) {\n      throw new IllegalArgumentException(\n          \"Rewrite rule must start with 'http://' or 'https://', but was '%s'\".formatted(rewrite));\n    }\n\n    if (!rewrite.toString().endsWith(\"/\")) {\n      throw new IllegalArgumentException(\n          \"Rewrite rule must end with '/', but was '%s'\".formatted(rewrite));\n    }\n  }\n\n  private static boolean isReservedHeaderName(String headerName) {\n    var normalizedHeader = headerName.toLowerCase(Locale.ROOT);\n    for (var reservedHeader : reservedHeaderNames) {\n      if (normalizedHeader.equals(reservedHeader)) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  private static boolean hasReservedHeaderPrefix(String headerName) {\n    var normalizedHeader = headerName.toLowerCase(Locale.ROOT);\n    for (var prefix : reservedHeaderPrefixes) {\n      if (normalizedHeader.startsWith(prefix)) {","sourceCodeStart":887,"sourceCodeEnd":923,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-core/src/main/java/org/pkl/core/util/IoUtils.java#L887-L923","documentation":"validateRewriteRule also requires rewrite rule URIs to end with '/' so that rule keys and targets behave as directory-like prefixes. This IllegalArgumentException is thrown when the rule URI is http(s) but lacks the trailing slash.","triggerScenarios":"validateRewriteRule receives e.g. https://localhost:8080/mypackage (no trailing slash) as a rewrite rule from --rewrite-settings or project config.","commonSituations":"Writing rewrite targets in config without the trailing slash, causing prefix matching to fail or be ambiguous.","solutions":["Append a trailing '/' to the rewrite rule URI in your configuration","Normalize the URI string before passing it to the API (add '/' if missing)","Re-run with the corrected value in PklProject.yml or CLI flags"],"exampleFix":"// before\nrewrite { [\"example.com/pkg/\"] = \"https://mirror.example/pkg\" }\n// after\nrewrite { [\"example.com/pkg/\"] = \"https://mirror.example/pkg/\" }","handlingStrategy":"validation","validationCode":"if (!ruleStr.endsWith(\"/\")) ruleStr = ruleStr + \"/\";","typeGuard":"function endsWithSlash(u) { return u.toString().endsWith(\"/\"); }","tryCatchPattern":"try { IoUtils.validateRewriteRule(rewrite) } catch (IllegalArgumentException e) { /* auto-append slash or report config error */ }","preventionTips":["Normalize rule URIs by appending '/' before validation","Store rewrite targets with trailing slashes in config templates","Add a config lint that enforces trailing slashes on prefix-style URIs"],"tags":["uri","config","rewrite"],"backgroundTag":"invalid-config-value","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}