apache/cassandra · error · ConfigurationException

Invalid class ' ': must extend or implement

Error message

Invalid %s class '%s': must extend or implement %s

What it means

classForNameWithoutInitialization loads a class without running its static initializers and verifies it extends/implements the expected base type. If the loaded class is not assignable to expectedType, a ConfigurationException reports the type mismatch with the required interface name. This guards pluggable configuration points against wrong types.

Solutions

  1. Point the config at a class that actually implements/extends the required type (named in the message)
  2. Fix your custom class to implement the expected interface
  3. Check for similarly-named classes from different plugin families and pick the right one
  4. Recompile/deploy the updated custom jar on all nodes

Example fix

// before
ssl_factory: com.example.MyAuditLogger  // wrong type
// after
ssl_factory: com.example.MySslContextFactory implements org.apache.cassandra.security.ISslContextFactory
Defensive patterns

Strategy: validation

Validate before calling

boolean isAssignable(String cn, Class<?> expected) {
    try { return expected.isAssignableFrom(Class.forName(cn, false, Thread.currentThread().getContextClassLoader())); }
    catch (Throwable t) { return false; }
}
// before config load: if (!isAssignable(sslFactoryName, ISslContextFactory.class)) failFast();

Type guard

boolean implementsPlugin(String cn, Class<?> expected) {
    try { return expected.isAssignableFrom(Class.forName(cn, false, Thread.currentThread().getContextClassLoader())); }
    catch (Throwable t) { return false; }
}

Try / catch

try {
    config.load();
} catch (ConfigurationException e) {
    if (e.getMessage() != null && e.getMessage().contains("must extend or implement"))
        logger.error("Configured class has the wrong type — fix class name in config", e);
    throw e;
}

Prevention

When it happens

Trigger: Configuring a class name for a pluggable component (e.g. SSL factory, crypto provider, commit log archiver) that exists but implements the wrong interface — classForNameWithoutInitialization(name, readable, expectedType, ...) hits the isAssignableFrom check.

Common situations: Implementing the wrong interface in custom code; pointing a factory option at a class of a different plugin kind; copy-pasting a class name between config sections (e.g. audit logger name used as ssl_factory); refactor moved functionality to a new interface.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/638be91b645534d9. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/utils/FBUtilities.java:819

     * Loads a class without initializing it, then verifies it extends or implements the expected base type.
     *
     * @return The Class for the given name.
     * @param classname Fully qualified classname.
     * @param readable Descriptive noun for the role the class plays.
     * @param expectedType Required superclass or interface.
     * @param classLoader ClassLoader to use.
     * @throws ConfigurationException If the class cannot be found or is not assignable to {@code expectedType}.
     */
    public static <T> Class<? extends T> classForNameWithoutInitialization(String classname,
                                                                           String readable,
                                                                           Class<T> expectedType,
                                                                           ClassLoader classLoader) throws ConfigurationException
    {
        try
        {
            Class<?> klass = Class.forName(classname, false, classLoader);
            if (!expectedType.isAssignableFrom(klass))
                throw new ConfigurationException(String.format("Invalid %s class '%s': must extend or implement %s",
                                                               readable,
                                                               classname,
                                                               expectedType.getName()));
            return klass.asSubclass(expectedType);
        }
        catch (ClassNotFoundException | NoClassDefFoundError e)
        {
            throw new ConfigurationException(String.format("Unable to find %s class '%s'", readable, classname), e);
        }
    }

    /**
     * Constructs an instance of the given class, which must have a no-arg or default constructor.
     * @param classname Fully qualified classname.
     * @param readable Descriptive noun for the role the class plays.
     * @throws ConfigurationException If the class cannot be found.
     */
    public static <T> T instanceOrConstruct(String classname, String readable) throws ConfigurationException

View on GitHub (pinned to 88fd0f6a0e)