apple/pkl · error · IOException

Is a directory

Error message

Is a directory

What it means

Raised in `PackageResolvers` `getBytes` when code attempts to read the bytes of a package element that is actually a directory (it has child elements rather than file content). The resolver throws `fileIsADirectory()` ("Is a directory") instead of returning bytes, because directories inside a package have no byte payload.

Solutions

  1. Point the path at a concrete file inside the package, not a directory.
  2. List elements first (e.g. `listElements`/`readGlob`) and pick a specific file.
  3. Append the missing trailing segment (e.g. `dir/` + `file.pkl`) to the URI.
  4. If you need directory contents, iterate the children keys rather than reading the directory itself.

Example fix

// before
bytes = read("pkg://example.com/my-pkg@1.0.0#/src")
// after
bytes = read("pkg://example.com/my-pkg@1.0.0#/src/config.pkl")
Defensive patterns

Strategy: validation

Validate before calling

function assertNotDirectory(pkgUri, path) {
  if (listElements(pkgUri).includes(path) && path.split("/").pop() === "") {
    throw new Error(`${path} is a directory; point at a file`);
  }
}

Type guard

function isFileElement(pkgUri, path) { return !path.endsWith("/") && listElements(pkgUri).some(e => e === path); }

Try / catch

try {
  const bytes = getBytes(pkgUri, path);
} catch (e) {
  if (e.code === "IsADirectory") { /* list elements and pick a file */ }
}

Prevention

When it happens

Trigger: Calling the package/resource resolution API with an element path that resolves to a directory node in the package's element tree — e.g. `read("pkg:/some/dir")` or a programmatic `getBytes` on a path ending at a folder, or a path missing the final filename segment.

Common situations: Treating `pkg:` URIs like filesystem paths and omitting the file name; expecting a directory listing or concatenation from `read` on a folder; typos where the path points at a parent directory of the intended file.

Related errors


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

Appendix: source

Thrown at pkl-core/src/main/java/org/pkl/core/packages/PackageResolvers.java:346

        throws IOException, SecurityManagerException {
      var packageUri = uri.getPackageUri();
      ensurePackageDownloaded(packageUri, checksums);
      TreePathElement elem;
      synchronized (lock) {
        elem = cachedTreePathElementRoots.get(uri.getPackageUri()).getElement(uri.getAssetPath());
      }
      if (elem == null) {
        throw new FileNotFoundException();
      } else if (elem.isDirectory()) {
        if (allowDirectories) {
          var text =
              StreamSupport.stream(elem.getChildren().getKeys().spliterator(), false)
                      .sorted()
                      .collect(Collectors.joining("\n"))
                  + "\n";
          return text.getBytes(StandardCharsets.UTF_8);
        }
        throw fileIsADirectory();
      }
      EconomicMap<String, ByteBuffer> entries;
      synchronized (lock) {
        entries = cachedEntries.get(packageUri);
      }
      // need to normalize here but not in `doListElements` nor `doHasElement` because
      // `TreePathElement.getElement` does normalization already.
      var path = IoUtils.toNormalizedPathString(Path.of(uri.getAssetPath()).normalize());
      return entries.get(path).array();
    }

    @Override
    public List<PathElement> doListElements(PackageAssetUri uri, @Nullable Checksums checksums)
        throws IOException, SecurityManagerException {
      var packageUri = uri.getPackageUri();
      ensurePackageDownloaded(packageUri, checksums);
      TreePathElement element;
      synchronized (lock) {

View on GitHub (pinned to f3efcbfc9b)