Micronaut native-image build: the three failed attempts before the fix, verbatim error text from the real `native-image` invocations (trimmed to the diagnostic message; the full stack scan is in each build's svm_err_*.md, not committed since it also repeats the full classpath). === Attempt 1 (no --initialize-at-build-time / --initialize-at-run-time args at all) === Failed generating 'micronaut-app' after 42.4s. > com.oracle.graal.pointsto.util.ParallelExecutionException: An object of type 'ch.qos.logback.classic.Logger' was found in the image heap. This type, however, is marked for initialization at image run time for the following reason: classes are initialized at run time by default. This is not allowed for correctness reasons: All objects that are stored in the image heap must be initialized at build time. ... Object was reached by scanning root constant ch.qos.logback.classic.Logger@7f585330: Logger[io.netty.util.ResourceLeakDetector] embedded in at io.netty.util.ResourceLeakDetector.needReport() [bci: -5] === Attempt 2 (--initialize-at-build-time=ch.qos.logback.classic.Logger) === Failed generating 'micronaut-app' after 46.0s. > com.oracle.graal.pointsto.util.ParallelExecutionException: An object of type 'ch.qos.logback.classic.LoggerContext' was found in the image heap. This type, however, is marked for initialization at image run time ... Object was reached by reading field ch.qos.logback.classic.Logger.loggerContext of constant ch.qos.logback.classic.Logger@6f5cb3e9: Logger[io.netty.util.ResourceLeakDetector] scanning root constant ch.qos.logback.classic.Logger@6f5cb3e9: Logger[io.netty.util.ResourceLeakDetector] embedded in at io.netty.util.ResourceLeakDetector.needReport() [bci: -5] === Attempt 3 (--initialize-at-build-time=ch.qos.logback [whole package]) === Failed generating 'micronaut-app' after 49.9s. > com.oracle.graal.pointsto.util.ParallelExecutionException: An object of type 'ch.qos.logback.classic.Logger' was found in the image heap. This type, however, is marked for initialization at image run time ... (identical trace to Attempt 1 - the package-level directive lost to a more specific --initialize-at-run-time directive for this exact class shipped in logback-classic's own META-INF/native-image/*/native-image.properties: GraalVM lets a more specific per-class directive override a broader package-level one, in either direction.) === Attempt 4 (--initialize-at-run-time=io.netty.util.ResourceLeakDetector,io.netty.util.ResourceLeakDetectorFactory) === Failed generating 'micronaut-app' after 42.9s. > com.oracle.graal.pointsto.util.ParallelExecutionException: An object of type 'io.netty.util.ResourceLeakDetector' was found in the image heap. This type, however, is marked for initialization at image run time ... Object was reached by scanning root constant io.netty.util.ResourceLeakDetector@2a2de2c0: io.netty.util.ResourceLeakDetector@2a2de2c0 embedded in io.netty.buffer.AbstractByteBufAllocator.toLeakAwareBuffer(AbstractByteBufAllocator.java:53) parsing method io.netty.buffer.AbstractByteBufAllocator.toLeakAwareBuffer(AbstractByteBufAllocator.java:53) reachable via the parsing context at static root method.(Unknown Source) (deferring just the leak detector to run-time wasn't enough: AbstractByteBufAllocator, itself build-time-initialized by default, holds a static reference to one.) === Fix that worked (--initialize-at-run-time=io.netty [whole package]) === Finished generating 'micronaut-app' in 7m 15s. 2,621 types -> 3,395 types reachable after the fix (more code stays reachable because initialization now genuinely happens at run time instead of being folded away), image heap 854MB peak RSS during the build, final executable 53.31MiB. Takeaway for the post: this is a build-time ordering conflict between two framework defaults, not a bug in either library - Netty wants its leak detector and buffer allocator built eagerly (for speed), logback wants its Logger/LoggerContext run-time (so file handles and config aren't baked into the image), and the two defaults only collide when both are on the classpath AND something (here, static root scanning during Micronaut's own analysis) forces Netty's allocator to be build-time reachable. Pushing the whole io.netty package to run-time init was the fix that actually stuck; chasing individual classes one GraalVM error message at a time was not.