apache/seatunnel · info

can't load jar use current thread classloader, use…

Error message

can't load jar use current thread classloader, use URLClassLoader instead now. message: 

What it means

When adding plugin jar URLs to the current thread's classloader fails (the classloader doesn't support addURL, e.g., it's not a URLClassLoader), the discovery falls back to creating a new URLClassLoader over the plugin jars. This warning records the original exception message before falling back.

Solutions

  1. This is a self-healing fallback — verify the plugin actually loads afterward; if it does, the warning can be ignored
  2. If plugin classes then fail to load, check for class isolation issues (classes loaded by different classloaders) and ensure the plugin's dependencies ship with it
  3. Run with a standard setup (bin/seatunnel.sh) where the context classloader supports URL addition
  4. Check the logged 'message:' text to identify the underlying addURL exception
Defensive patterns

Strategy: fallback

Validate before calling

ClassLoader cl = Thread.currentThread().getContextClassLoader();
boolean canAddUrl = cl instanceof URLClassLoader;

Type guard

if (!(Thread.currentThread().getContextClassLoader() instanceof URLClassLoader)) { /* expect fallback path */ }

Prevention

When it happens

Trigger: createOptionalPluginInstance tried to append plugin jar paths to the thread context classloader, but the classloader type does not support URL appending (or a security/IO exception occurred), so it constructs a URLClassLoader instead.

Common situations: Running under application servers or module systems (e.g., OSGi-ish or JPMS) where the context classloader is not a URLClassLoader; engine wrappers that replace the default classloader.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/9ba9812dbede8cf6. Report an issue: GitHub.

Appendix: source

Thrown at seatunnel-plugin-discovery/src/main/java/org/apache/seatunnel/plugin/discovery/AbstractPluginDiscovery.java:235

    @Override
    public Optional<T> createOptionalPluginInstance(
            PluginIdentifier pluginIdentifier, Collection<URL> pluginJars) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        T pluginInstance = loadPluginInstance(pluginIdentifier, classLoader);
        if (pluginInstance != null) {
            log.info("Load plugin: {} from classpath", pluginIdentifier);
            return Optional.of(pluginInstance);
        }
        Optional<List<URL>> pluginJarPaths = getPluginJarPath(pluginIdentifier);
        // if the plugin jar not exist in classpath, will load from plugin dir.
        if (pluginJarPaths.isPresent()) {
            try {
                // use current thread classloader to avoid different classloader load same class
                // error.
                addURLToClassLoaderConsumer.accept(classLoader, pluginJarPaths.get());
                addURLToClassLoaderConsumer.accept(classLoader, (List<URL>) pluginJars);
            } catch (Exception e) {
                log.warn(
                        "can't load jar use current thread classloader, use URLClassLoader instead now."
                                + " message: "
                                + e.getMessage());
                URL[] urls = new URL[pluginJars.size() + 1];
                int i = 0;
                for (URL pluginJar : pluginJars) {
                    urls[i++] = pluginJar;
                }
                urls =
                        Stream.concat(Arrays.stream(urls), pluginJarPaths.get().stream())
                                .distinct()
                                .toArray(URL[]::new);
                classLoader =
                        new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());
            }
            pluginInstance = loadPluginInstance(pluginIdentifier, classLoader);
            if (pluginInstance != null) {
                log.info(

View on GitHub (pinned to cf67b549a7)