apache/flink · error · IllegalStateException

Trying to access closed classloader. Please check if you sto

Error message

Trying to access closed classloader. Please check if you store classloaders directly or indirectly in static fields. If the stacktrace suggests that the leak occurs in a third party library and cannot be fixed immediately, you can disable this check with the configuration '{CoreOptions.CHECK_LEAKED_CLASSLOADER.key()}'.

What it means

SafetyNetWrapperClassLoader.ensureInner fires IllegalStateException when a user-code class loader is used after it was closed. Flink closes user classloaders when a job reaches a terminal state to detect classloader leaks; any later loadClass/resource call through the wrapper hits inner == null and this message tells you a static field, ThreadLocal, or cached object still references the loader. The message also names the escape hatch: the 'classloader.check-leaked-classloader' config key (CoreOptions.CHECK_LEAKED_CLASSLOADER).

Source

Thrown at flink-core/src/main/java/org/apache/flink/util/FlinkUserCodeClassLoaders.java:184

            this.inner = inner;
        }

        @Override
        public void close() {
            final FlinkUserCodeClassLoader inner = this.inner;
            if (inner != null) {
                try {
                    inner.close();
                } catch (IOException e) {
                    LOG.warn("Could not close user classloader", e);
                }
            }
            this.inner = null;
        }

        private FlinkUserCodeClassLoader ensureInner() {
            if (inner == null) {
                throw new IllegalStateException(
                        "Trying to access closed classloader. Please check if you store "
                                + "classloaders directly or indirectly in static fields. If the stacktrace suggests that the leak "
                                + "occurs in a third party library and cannot be fixed immediately, you can disable this check "
                                + "with the configuration '"
                                + CoreOptions.CHECK_LEAKED_CLASSLOADER.key()
                                + "'.");
            }
            return inner;
        }

        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            return ensureInner().loadClass(name);
        }

        @Override
        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
            // called for dynamic class loading

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Find the holder from the stack trace: clear the static field / ThreadLocal / cache when the job's close() or RichFunction.close() runs
  2. As a temporary workaround set classloader.check-leaked-classloader: false — the leak remains but the check no longer throws (memory still grows on session clusters)
  3. Move the offending library's initialization to the parent classloader (e.g. put the jar in Flink's lib/ instead of the user jar)
  4. Ensure threads and executors created in user code are shut down in close() so nothing outlives the classloader

Example fix

// before
public class MyFunction extends RichMapFunction<..> {
    private static final ObjectMapper MAPPER = new ObjectMapper(); // can pin loader
}
// after
public class MyFunction extends RichMapFunction<..> {
    private ObjectMapper mapper; // instance field, created in open(), released with the loader
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    Class<?> c = userClassLoader.loadClass(name);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("closed classloader")) { /* job already terminal: abandon cached loader */ }
}

Prevention

When it happens

Trigger: A job class or third-party library caches the context classloader, a class, or an object holding a class reference in a static/ThreadLocal field; after the job finishes/fails and Flink closes the loader, a lingering thread or shutdown hook triggers classloading through it.

Common situations: Libraries with static caches ( JDBC drivers, logging configs, caches like Guava/Caffeine with strong refs, Akka/Pekko, Jackson mappers capturing the loader), driver threads outliving the job, or reused executors inside user code on session clusters.

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/199dd0f850a07a88. Report an issue: GitHub.