{"record":{"id":"e23d55d167a85315","repo":"quarkusio/quarkus","slug":"osgi-is-not-supported-by-quarkus-liquibase","errorCode":null,"errorMessage":"OSGi is not supported by quarkus-liquibase","messagePattern":"OSGi is not supported by quarkus-liquibase","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"extensions/liquibase/liquibase-common/src/main/java/io/quarkus/liquibase/common/runtime/graal/SubstituteOsgiUtil.java","lineNumber":11,"sourceCode":"package io.quarkus.liquibase.common.runtime.graal;\n\nimport com.oracle.svm.core.annotate.Substitute;\nimport com.oracle.svm.core.annotate.TargetClass;\n\n@TargetClass(liquibase.util.OsgiUtil.class)\nfinal class SubstituteOsgiUtil {\n\n    @Substitute\n    public static <T> Class<T> loadClass(String className) throws ClassNotFoundException {\n        throw new UnsupportedOperationException(\"OSGi is not supported by quarkus-liquibase\");\n    }\n}\n","sourceCodeStart":1,"sourceCodeEnd":14,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/liquibase/liquibase-common/src/main/java/io/quarkus/liquibase/common/runtime/graal/SubstituteOsgiUtil.java#L1-L14","documentation":"quarkus-liquibase substitutes Liquibase's liquibase.util.OsgiUtil.loadClass with a GraalVM @Substitute that always throws UnsupportedOperationException, because OSGi classloading is meaningless in a native Quarkus image. It only fires when Liquibase code running inside a native executable attempts to resolve a class through its OSGi path instead of the plain Class.forName path. This is intentionally unsupported behavior, not a bug.","triggerScenarios":"Running a Quarkus application in native-image mode where Liquibase calls OsgiUtil.loadClass(className) — typically during changelog parsing/custom class resolution (e.g. custom ChangeLogParser, ChangeGenerator, or extension classes registered only via OSGi service discovery) in a native build.","commonSituations":"Users shipping Liquibase extensions or custom classes (CustomChangeWrapper, custom task/changes) that Liquibase tries to load via the OSGi branch in native mode; unusual Liquibase versions where class lookup prefers the OSGi path; native-image builds where no substitute for the actual loading path exists.","solutions":["Verify the load happens only in native mode; test the same flow in JVM mode to isolate the OSGi substitute path","Remove or restructure the custom Liquibase extension/class so it is discovered via standard ServiceLoader/Class.forName instead of OSGi (register it via META-INF/services or liquibase.locator ServiceLocator)","Pin/upgrade liquibase version bundled by quarkus-liquibase so the OSGi code path is not taken","If the class is truly needed, add your own GraalVM substitution/feature for liquibase.util.OsgiUtil or the calling code in your app"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Native apps: ensure custom Liquibase classes are loaded via ServiceLoader before Liquibase runs\nif (Class.forName(\"my.pkg.MyCustomChange\", true, Thread.currentThread().getContextClassLoader()) == null) {\n    throw new IllegalStateException(\"Custom Liquibase class not on classpath; OsgiUtil path will fail in native\");\n}","typeGuard":"boolean isNativeImage = System.getProperty(\"org.graalvm.nativeimage.iscompilation\") != null\n        || Class.forName(\"com.oracle.svm.core.annotate.Substitute\", false, ClassLoader.getSystemClassLoader()) != null;","tryCatchPattern":"try {\n    liquibase.update(context);\n} catch (UnsupportedOperationException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"OSGi is not supported\")) {\n        throw new IllegalStateException(\"Liquibase tried OSGi classloading in native mode; register classes via ServiceLoader\", e);\n    }\n    throw e;\n}","preventionTips":["Avoid custom Liquibase classes resolved through OSGi ServiceLocator in native builds","Register Liquibase extensions in META-INF/services so plain Class.forName/ServiceLoader discovery is used","Always test Liquibase flows in native-image before production","Pin Liquibase versions known to work with quarkus-liquibase native substitution"],"tags":["native-image","graalvm","liquibase","osgi","unsupported"],"backgroundTag":"unsupported-operation-in-native-image","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}