{"record":{"id":"26294ddeca755905","repo":"oracle/graal","slug":"loading-constraint-violated","errorCode":null,"errorMessage":"Loading constraint violated !","messagePattern":"Loading constraint violated !","errorType":"exception","errorClass":"LoadingConstraintViolationException","httpStatus":null,"severity":"error","filePath":"espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/constraints/LoadingConstraintsShared.java","lineNumber":208,"sourceCode":"     * <p>\n     * As such, the entire set of constraints for a particular type, called a bucket, is a list of\n     * constraints, one per Klass instance of this type. Each such constraint record all class\n     * loaders that resolves the type as the recorded Klass instance.\n     * <p>\n     * To represent this, we use a map from types to buckets. Buckets are a doubly linked list. To\n     * support concurrency, we use a ConcurrentHashMap. Failure to insert an item in the map due to\n     * concurrency simply means someone was faster than us, we can therefore simply use the one that\n     * is already present.\n     * <p>\n     * Once the bucket is obtained, we immediately synchronize on it. This prevents concurrency\n     * problems as a whole, while allowing multiple threads to do constraint checking on different\n     * types.\n     */\n    private final ConcurrentHashMap<Symbol<Type>, ConstraintBucket<Loader, Storage>> pairings = new ConcurrentHashMap<>();\n\n    private void checkOrAdd(Symbol<Type> type, long k1, long k2, Loader loader1, Loader loader2) throws LoadingConstraintViolationException {\n        if (exists(k1) && exists(k2) && k1 != k2) {\n            throw new LoadingConstraintViolationException(\"Loading constraint violated !\");\n        }\n        long klass = !exists(k1) ? k2 : k1;\n        ConstraintBucket<Loader, Storage> bucket = lookup(type);\n        if (bucket == null) {\n            Constraint<Loader, Storage> newConstraint = Constraint.create(this, klass, loader1, loader2);\n            bucket = new ConstraintBucket<>(newConstraint);\n            ConstraintBucket<Loader, Storage> previous = pairings.putIfAbsent(type, bucket);\n            if (previous != null) {\n                bucket = previous;\n            }\n        }\n        synchronized (bucket) {\n            Constraint<Loader, Storage> c1 = bucket.lookupLoader(this, loader1);\n            klass = checkConstraint(klass, c1);\n            Constraint<Loader, Storage> c2 = bucket.lookupLoader(this, loader2);\n            klass = checkConstraint(klass, c2);\n            if (c1 == null && c2 == null) {\n                bucket.add(Constraint.create(this, klass, loader1, loader2));","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/espresso-shared/src/com.oracle.truffle.espresso.shared/src/com/oracle/truffle/espresso/shared/constraints/LoadingConstraintsShared.java#L190-L226","documentation":"The JVM spec (JVMS 5.4.3/5.3) records loading constraints so that a type name used across different class loaders denotes the same runtime type. LoadingConstraintsShared.checkOrAdd registers/validates such a constraint per Symbol<Type>; when both keys k1 and k2 already resolve to existing (different) classes, the constraint cannot hold and a LoadingConstraintViolationException is thrown.","triggerScenarios":"Constraint-deriving operations (method resolution, invokevirtual/invokeinterface, checkcast on constrained signatures) with multiple class loaders where loader1 and loader2 resolve the same type name to different Class objects (k1 != k2, both already loaded).","commonSituations":"Custom loader hierarchies with child-first delegation; the same library/class duplicated on classpaths visible to two loaders; hot-reload or plugin containers (OSGi-like) where an older class version stays loaded in one loader.","solutions":["Make the constrained class loadable by exactly one loader: fix delegation (parent-first) so both loaders share the same Class instance.","Remove duplicate copies of the class/jar from the classpaths of the involved loaders.","If the constraint comes from a redefinition/redeployment, drop references and unload the stale class version (or restart the context).","Catch the resulting LinkageError at the integration boundary to fail that operation instead of the whole VM."],"exampleFix":"// before: child-first loader loads its own copy of api.Foo -> constraint violation on cross-loader call\n\n// after: delegate shared API packages to the parent loader\nclass ChildLoader extends ClassLoader {\n    @Override\n    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {\n        if (name.startsWith(\"api.\")) return super.loadClass(name, resolve); // parent-first for shared types\n        return findClass(name);\n    }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    crossLoaderInvoke();\n} catch (LinkageError e) {\n    // LoadingConstraintViolationException surfaces as a LinkageError subtype\n    if (e.getMessage() != null && e.getMessage().contains(\"Loading constraint\")) {\n        reportLoaderConflict(e); // identify the two loaders / type name involved\n    }\n    throw e;\n}","preventionTips":["Load shared API types through a single parent loader (parent-first delegation).","Keep exactly one copy of each shared class on the combined classpaths.","Drop old loaders fully on redeploy so stale classes cannot participate in constraints."],"tags":["jvm","classloading","espresso","linkage","concurrency"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}