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 loadingView on GitHub (pinned to 2f3c205e92)
Solutions
- Find the holder from the stack trace: clear the static field / ThreadLocal / cache when the job's close() or RichFunction.close() runs
- 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)
- Move the offending library's initialization to the parent classloader (e.g. put the jar in Flink's lib/ instead of the user jar)
- 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
- Never store classes, classloaders, or instances of user classes in static/ThreadLocal fields from library code
- Release caches and shut down threads/executors in close() lifecycle methods
- Run shared heavy libraries from lib/ (parent loader) instead of the user jar
- Use classloader.check-leaked-classloader: false only as a temporary measure — the leak remains
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
- Cannot deserialize and unwrap accumulators properly.
- Failed to deserialize coordination response
- Cannot deserialize and unwrap accumulators properly.
- Source fetch execution was interrupted
- The thread was interrupted while waiting for a fetcher task.
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/199dd0f850a07a88.
Report an issue: GitHub.