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
- 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
- Drop one of the two jars so the package is defined from a single source
- 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
- Make the Sealed manifest attribute consistent (present or absent) across all jars sharing a package
- Rebuild artifact sets atomically instead of mixing old and new versions on one class path
- Automate manifest audits in CI for image builds with multi-jar class paths
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
- Sealing violation: package %s is sealed
- Given URI '%s' cannot be expressed as URL.
- Type with name %s not found.
- %s is not an enum type
- SpectrePHTBarriers can be set to 'AllTargets' if and only if
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/cc29ebc63039a405.
Report an issue: GitHub.