oracle/graal · error · SecurityException

Sealing violation: can't seal package %s: already loaded

Error message

Sealing violation: can't seal package %s: already loaded

What it means

Companion of the sealed-package check: if the package was previously defined WITHOUT sealing, JPMS/URLClassLoader semantics forbid sealing it later. When a second jar's manifest declares the package sealed (isSealed(pkgname, man) is true) but the already-loaded package is unsealed, this SecurityException is thrown — package sealing must be consistent from first definition.

Source

Thrown at compiler/src/jdk.graal.compiler.hostvmaccess/src/jdk/graal/compiler/hostvmaccess/HostVMAccessClassLoader.java:505

            CodeSigner[] signers = res.getCodeSigners();
            CodeSource cs = new CodeSource(url, signers);
            return defineClass(name, b, 0, b.length, cs);
        }
    }

    /**
     * See {@code java.net.URLClassLoader#getAndVerifyPackage}.
     */
    private Package getAndVerifyPackage(String pkgname, Manifest man, URL url) {
        Package pkg = getDefinedPackage(pkgname);
        if (pkg != null) {
            if (pkg.isSealed()) {
                if (!pkg.isSealed(url)) {
                    throw new SecurityException("Sealing violation: package " + pkgname + " is sealed");
                }
            } else {
                if ((man != null) && isSealed(pkgname, man)) {
                    throw new SecurityException("Sealing violation: can't seal package " + pkgname + ": already loaded");
                }
            }
        }
        return pkg;
    }

    /**
     * See {@code java.net.URLClassLoader#definePackage}.
     */
    private Package definePackage(String name, Manifest man, URL url) {
        String specTitle = null;
        String specVersion = null;
        String specVendor = null;
        String implTitle = null;
        String implVersion = null;
        String implVendor = null;
        String sealed = null;
        URL sealBase = null;

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Make sealing consistent: remove 'Sealed: true' from the later jar's manifest, or ensure the FIRST jar that defines the package is the sealed one
  2. Drop one of the two jars so the package is defined from a single source
  3. Rebuild the artifact set so all entries for that package come from one manifest configuration

Example fix

# before
classpath: lib-core.jar     # unsealed, defines com.acme.api first
classpath: lib-secure.jar   # manifest: Sealed: true for com.acme.api -> SecurityException

# after
classpath: lib-core.jar     # single, consistent source for com.acme.api
Defensive patterns

Strategy: validation

Validate before calling

static void checkSealingConsistency(List<Path> classpath) throws IOException {
    Map<String, String> pkgFirstSource = new HashMap<>();
    for (Path jar : classpath) {
        try (JarFile jf = new JarFile(jar.toFile())) {
            Manifest man = jf.getManifest();
            boolean seals = man != null && Boolean.parseBoolean(man.getMainAttributes().getValue("Sealed"));
            Enumeration<JarEntry> es = jf.entries();
            while (es.hasMoreElements()) {
                String n = es.nextElement().getName();
                if (n.endsWith(".class")) {
                    String pkg = n.substring(0, Math.max(0, n.lastIndexOf('/'))).replace('/', '.');
                    pkgFirstSource.merge(pkg, seals ? "sealed" : "open", (a, b) -> a.equals(b) ? a : "CONFLICT:" + pkg);
                }
            }
        }
    }
}

Try / catch

catch (SecurityException e) { if (e.getMessage().contains("already loaded")) { align 'Sealed' attributes across the jars sharing the package or drop one jar; } }

Prevention

When it happens

Trigger: First class-path entry defines classes in package P with an unsealed (or absent) manifest; a later entry also contains package P and its manifest marks P as sealed. getAndVerifyPackage hits the else-branch: pkg exists, not sealed, but new manifest seals it.

Common situations: Mixing an original dependency jar with a repackaged/signed version that adds 'Sealed: true'; incremental builds where a rebuilt jar gains sealing metadata while stale classes from the unsealed original are still on the path.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/cc29ebc63039a405. Report an issue: GitHub.