apache/pulsar · error · IllegalArgumentException

Could not find sink config class

Error message

Could not find sink config class

What it means

Thrown by SinkConfigUtils.validateAndExtractDetails when the connector definition declares a sinkConfigClass that cannot be loaded via Class.forName using the sink function's classloader (ClassNotFoundException is rewrapped as IllegalArgumentException). The connector's custom config class is required to validate the sink's user-provided configs.

Source

Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SinkConfigUtils.java:752

        }
        if (newConfig.getTransformFunctionConfig() != null) {
            mergedConfig.setTransformFunctionConfig(newConfig.getTransformFunctionConfig());
        }
        if (newConfig.getSourceSubscriptionPosition() != null) {
            mergedConfig.setSourceSubscriptionPosition(newConfig.getSourceSubscriptionPosition());
        }
        return mergedConfig;
    }

    public static void validateSinkConfig(SinkConfig sinkConfig, ValidatableFunctionPackage sinkFunction) {
        try {
            ConnectorDefinition defn = sinkFunction.getFunctionMetaData(ConnectorDefinition.class);
            if (defn != null && defn.getSinkConfigClass() != null) {
                Class<?> configClass = Class.forName(defn.getSinkConfigClass(), true, sinkFunction.getClassLoader());
                validateSinkConfig(sinkConfig, configClass);
            }
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("Could not find sink config class", e);
        }
    }

    public static void validateSinkConfig(SinkConfig sinkConfig, Class<?> configClass) {
        try {
            Object configObject =
                    ObjectMapperFactory.getMapper().getObjectMapper()
                            .convertValue(sinkConfig.getConfigs(), configClass);
            if (configObject != null) {
                ConfigValidation.validateConfig(configObject);
            }
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Could not validate sink config: " + e.getMessage());
        }
    }
}

View on GitHub (pinned to 820761864e)

Solutions

  1. Ensure the connector NAR contains the declared sinkConfigClass and matches the deployed version
  2. Correct the sinkConfigClass name in the connector definition (META-INF/services/connector yaml)
  3. Check the sinkFunction classloader includes the connector's jar; restart with the correct NAR

Example fix

// connector definition yaml
// before
sinkConfigClass: com.example.OldSinkConfig
// after
sinkConfigClass: com.example.MySinkConfig // class actually packaged in the NAR
Defensive patterns

Strategy: try-catch

Validate before calling

try {
    Class.forName(sinkConfigClassFqcn, true, fnClassLoader);
} catch (ClassNotFoundException e) {
    throw new IllegalStateException("Connector NAR is missing config class " + sinkConfigClassFqcn);
}

Try / catch

try {
    admin.sinks().updateSink(tenant, namespace, cfg, null);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Could not find sink config class")) { /* redeploy correct connector NAR */ }
    else throw e;
}

Prevention

When it happens

Trigger: Updating/submitting a sink backed by a connector whose ConnectorDefinition.getSinkConfigClass() names a class missing from the classpath/NAR, e.g. wrong NAR file version or class renamed in the connector.

Common situations: Custom connector NAR without the config class packaged; connector upgraded and config class renamed while old configs reference the old FQCN; typo in the sinkConfigClass declared in the connector's yaml.

Related errors


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