apple/pkl · error · IllegalStateException

Package resolver has already been closed.

Error message

Package resolver has already been closed.

What it means

IllegalStateException thrown by checkNotClosed when a package resolver's API (getDependencyMetadata, listElements, hasElement) is used after the resolver was closed. Resolvers are one-shot resources; once closed, all further lookups are invalid.

Solutions

  1. Do not use the resolver after calling close(); obtain a fresh resolver instance for subsequent operations
  2. Restructure code so all resolver calls happen inside the try block of a try-with-resources statement
  3. Check for premature close() calls (e.g. in finally blocks or shutdown hooks) that run before remaining lookups
  4. Ensure concurrent code doesn't share one resolver across threads with differing lifetimes

Example fix

// before
var resolver = PackageResolvers.open(...);
resolver.close();
resolver.getDependencyMetadata(uri); // throws
// after
try (var resolver = PackageResolvers.open(...)) {
  resolver.getDependencyMetadata(uri);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// guard before use
if (resolver.isClosed()) throw new IllegalStateException("resolver closed");

Type guard

boolean isUsable(PackageResolver r) { return r != null && !r.isClosed(); }

Try / catch

try {
  resolver.getDependencyMetadata(uri);
} catch (IllegalStateException e) {
  if (e.getMessage().contains("already been closed")) {
    resolver = reopenResolver(); // reacquire and retry once
  }
}

Prevention

When it happens

Trigger: Calling getDependencyMetadata, listElements, or hasElement on a PackageResolver after close() was called on it (or on its parent ProjectPackageResolver / resolver manager).

Common situations: Holding a resolver reference beyond the scope that closed it; closing a resolver in a finally block and reusing it afterwards; sharing a resolver across concurrent tasks where one task closes it early.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

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

    protected abstract List<PathElement> doListElements(
        PackageAssetUri uri, @Nullable Checksums checksums)
        throws IOException, SecurityManagerException;

    protected abstract boolean doHasElement(PackageAssetUri uri, @Nullable Checksums checksums)
        throws IOException, SecurityManagerException;

    protected PackageLoadError invalidPackageZipUrl(
        PackageUri packageUri, DependencyMetadata dependencyMetadata) {
      return new PackageLoadError(
          "invalidPackageZipUrl",
          packageUri.getDisplayName(),
          dependencyMetadata.getPackageZipUrl());
    }

    private void checkNotClosed() {
      if (isClosed.get()) {
        throw new IllegalStateException("Package resolver has already been closed.");
      }
    }
  }

  /**
   * A package resolver that holds entire package contents in memory.
   *
   * <p>This gets used when the cache dir is not set.
   */
  static final class InMemoryPackageResolver extends AbstractPackageResolver {
    @GuardedBy("lock")
    private final EconomicMap<PackageUri, EconomicMap<String, ByteBuffer>> cachedEntries =
        EconomicMaps.create();

    @GuardedBy("lock")
    private final EconomicMap<PackageUri, TreePathElement> cachedTreePathElementRoots =
        EconomicMaps.create();

View on GitHub (pinned to f3efcbfc9b)