apache/pulsar · error · IllegalArgumentException

Could not instantiate <configKeyName> '<factoryClassName>'

Error message

Could not instantiate <configKeyName> '<factoryClassName>'

What it means

instantiateNamedFactory loads a PulsarTlsFactory implementation class by name via Class.forName (preferring the thread context classloader) and instantiates it with a no-arg constructor. If the class cannot be loaded, is not a PulsarTlsFactory, lacks a no-arg constructor, or its constructor fails, an IllegalArgumentException is thrown naming the config key and class.

Source

Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/tls/ClientTlsFactorySupport.java:341

     * Reflectively instantiate a named {@link PulsarTlsFactory} via its public no-arg constructor, failing
     * loudly (an {@link IllegalArgumentException} naming the config key) when it cannot be instantiated.
     *
     * @param factoryClassName the factory class name to instantiate
     * @param configKeyName    the config key naming it (for the failure message)
     * @return the instantiated factory
     */
    static PulsarTlsFactory instantiateNamedFactory(String factoryClassName, String configKeyName) {
        String name = factoryClassName.trim();
        // FIX G: honor the thread context classloader (PulsarAdminImpl sets it to the plugin loader via
        // setContextClassLoader) so a factory class visible only through a custom TCCL is found. Plain
        // Class.forName(name) uses only this class's defining loader and misses TCCL-only classes. Fall back
        // to the defining loader when no TCCL is set.
        ClassLoader tccl = Thread.currentThread().getContextClassLoader();
        try {
            Class<?> clazz = tccl != null ? Class.forName(name, true, tccl) : Class.forName(name);
            return (PulsarTlsFactory) clazz.getConstructor().newInstance();
        } catch (ReflectiveOperationException e) {
            throw new IllegalArgumentException(
                    "Could not instantiate " + configKeyName + " '" + factoryClassName + "'", e);
        }
    }

    /**
     * Parse a {@code tlsFactoryConfig} string into the factory init params map (mirrors the server-side
     * {@code TlsFactorySupport.parseFactoryConfig}). A blank value yields an empty map; a value starting with
     * <code>{</code> is parsed as a JSON object; otherwise it is parsed as a comma-separated
     * {@code key=value} list.
     *
     * @param tlsFactoryConfig the configured factory params (may be null/blank)
     * @return an immutable params map (possibly empty)
     */
    static Map<String, String> parseFactoryConfig(String tlsFactoryConfig) {
        if (StringUtils.isBlank(tlsFactoryConfig)) {
            return Map.of();
        }
        String trimmed = tlsFactoryConfig.trim();

View on GitHub (pinned to 820761864e)

Solutions

  1. Verify the fully-qualified class name in the config is correct and spelled exactly.
  2. Ensure the factory class is on the client classpath and visible to the thread context classloader.
  3. Give the factory a public no-arg constructor (parameterless) implementing PulsarTlsFactory.
  4. Check the factory constructor for throwing code (bad config files, etc.) and fix its initialization.

Example fix

// before
class MyTlsFactory implements PulsarTlsFactory {
    public MyTlsFactory(String configPath) { ... }
}
// after
class MyTlsFactory implements PulsarTlsFactory {
    public MyTlsFactory() { ... }
    public void configure(Map<String,String> params) { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

try {
    Class<?> c = Class.forName(factoryClassName);
    if (!PulsarTlsFactory.class.isAssignableFrom(c)) throw new IllegalArgumentException("not a PulsarTlsFactory");
    c.getDeclaredConstructor(); // must exist and be public
} catch (ClassNotFoundException | NoSuchMethodException e) {
    throw new IllegalArgumentException("Bad tls factory class: " + factoryClassName, e);
}

Try / catch

try {
    buildClientWithTlsFactory(name);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Could not instantiate")) {
        log.error("Check class name, classpath, and no-arg constructor", e);
    }
}

Prevention

When it happens

Trigger: Setting a client TLS factory config key (e.g. tlsFactoryClassName-style configKeyName) to a class name that does not exist, is abstract, has no public no-arg constructor, throws in its constructor, or is not visible to the TCCL.

Common situations: Typo in the fully-qualified class name; class not on the client classpath; custom factory with only a parameterized constructor; shaded/uber-jar excluding the factory class; factory constructor throwing due to bad init.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/dca22e703100f614. Report an issue: GitHub.