apple/pkl · error · URISyntaxException

invalidRelativePathInPackageUri

invalidRelativePathInPackageUri

Error message

invalidRelativePathInPackageUri

What it means

Thrown by the PackageUri constructor when the package URI path contains a '..' segment (literally or percent-encoded). Parent-directory segments are rejected to keep package URIs canonical and prevent path traversal within package identifiers.

Source

Thrown at pkl-core/src/main/java/org/pkl/core/packages/PackageUri.java:67

    var scheme = uri.getScheme();
    if (scheme == null || !(scheme.equals("package") || scheme.equals("projectpackage"))) {
      throw new URISyntaxException(
          uri.toString(), ErrorMessages.create("invalidSchemeInPackageUri", scheme));
    }
    var authority = uri.getAuthority();
    if (authority == null || authority.isEmpty()) {
      throw new URISyntaxException(
          uri.toString(), ErrorMessages.create("missingAuthorityInPackageUri", uri));
    }
    var path = uri.getPath();
    if (path == null || path.isEmpty()) {
      throw new URISyntaxException(
          uri.toString(), ErrorMessages.create("missingPathInPackageUri", uri));
    }
    // reject `..` segments, percent-encoded or not
    for (var segment : path.split("/", -1)) {
      if (segment.equals("..")) {
        throw new URISyntaxException(
            uri.toString(), ErrorMessages.create("invalidRelativePathInPackageUri"));
      }
    }
    var versionIdx = path.lastIndexOf('@');
    if (versionIdx == -1) {
      throw new URISyntaxException(
          uri.toString(), ErrorMessages.create("missingVersionInPackageUri", path));
    }
    this.uri = IoUtils.stripFragment(uri);
    this.pathWithoutVersion = path.substring(0, versionIdx);
    var checksumIdx = path.indexOf("::");
    var versionPart = path.substring(versionIdx + 1);
    if (checksumIdx > versionIdx) {
      var checksumPart = path.substring(checksumIdx + 2);
      versionPart = path.substring(versionIdx + 1, checksumIdx);
      this.checksums = parseChecksumPart(checksumPart);
    }
    try {

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Remove all '..' segments and use a canonical absolute package path
  2. Normalize the path (or reject the input) before constructing the PackageUri
  3. Use the package name exactly as published instead of a computed relative path

Example fix

// before
var uri = URI.create("package://example.com/a/../my-pkg@1.2.3");
var pkg = new PackageUri(uri);
// after
var uri = URI.create("package://example.com/my-pkg@1.2.3");
var pkg = new PackageUri(uri);
Defensive patterns

Strategy: validation

Validate before calling

boolean hasNoDotDotSegments(String path) {
  for (var segment : path.split("/", -1)) {
    if (segment.equals("..")) return false;
  }
  return true;
}

Type guard

static boolean isCanonicalPackagePath(URI uri) {
  var path = uri.getPath();
  if (path == null) return false;
  for (var segment : path.split("/", -1)) {
    if (segment.equals("..")) return false;
  }
  return true;
}

Try / catch

try {
  var pkg = new PackageUri(uri);
} catch (URISyntaxException e) {
  throw new IllegalArgumentException("Package URI must not contain '..' segments: " + uri, e);
}

Prevention

When it happens

Trigger: new PackageUri(uri) where any path segment split on '/' equals '..', e.g. 'package://example.com/../other@1.0.0' or with %2E%2E encoded segments.

Common situations: Programmatically resolving relative paths and forgetting normalization before building the URI; template/concatenation bugs injecting '..'; attempting to smuggle traversal segments into a dependency URI (caught by design).

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08). Data as JSON: /api/errors/b56d535cb346d37d. Report an issue: GitHub.