apache/pulsar · error · IllegalArgumentException
Failed to extract sink class from archive
Error message
Failed to extract sink class from archive
What it means
When className is unset, validateAndExtractDetails reads the connector definition from META-INF/services/pulsar-io.yaml; if the YAML parses but declares no sinkClass (e.g. it only defines a source, or the sinkClass field is empty), this IllegalArgumentException is thrown because a sink deployment cannot proceed without an implementing class.
Source
Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SinkConfigUtils.java:469
ResourceConfigUtils.validate(sinkConfig.getResources());
}
if (sinkConfig.getTimeoutMs() != null && sinkConfig.getTimeoutMs() < 0) {
throw new IllegalArgumentException("Sink timeout must be a positive number");
}
String sinkClassName = sinkConfig.getClassName();
// if class name in sink config is not set, this should be a built-in sink
// thus we should try to find it class name in the NAR service definition
if (sinkClassName == null) {
ConnectorDefinition connectorDefinition = sinkFunction.getFunctionMetaData(ConnectorDefinition.class);
if (connectorDefinition == null) {
throw new IllegalArgumentException(
"Sink package doesn't contain the META-INF/services/pulsar-io.yaml file.");
}
sinkClassName = connectorDefinition.getSinkClass();
if (sinkClassName == null) {
throw new IllegalArgumentException("Failed to extract sink class from archive");
}
}
// check if sink implements the correct interfaces
TypeDefinition sinkClass;
try {
sinkClass = sinkFunction.resolveType(sinkClassName);
} catch (TypePool.Resolution.NoSuchTypeException e) {
throw new IllegalArgumentException(
String.format("Sink class %s not found", sinkClassName), e);
}
String functionClassName = sinkConfig.getTransformFunctionClassName();
TypeDefinition typeArg;
ValidatableFunctionPackage inputFunction;
if (transformFunction != null) {
// if function class name in sink config is not set, this should be a built-in function
// thus we should try to find it class name in the NAR service definitionView on GitHub (pinned to 820761864e)
Solutions
- Add/fix the sinkClass entry in META-INF/services/pulsar-io.yaml inside the NAR.
- Use the correct archive — one that implements a sink (or set className explicitly to your sink class).
- If the connector is source-only, deploy it via 'pulsar-admin source create' instead.
- Inspect the YAML: 'unzip -p my-sink.nar META-INF/services/pulsar-io.yaml' to confirm sinkClass is present.
Example fix
// before — META-INF/services/pulsar-io.yaml name: my-connector sourceClass: org.example.MySource // after name: my-connector sourceClass: org.example.MySource sinkClass: org.example.MySink
Defensive patterns
Strategy: validation
Validate before calling
ConnectorDefinition def = sinkPkg.getFunctionMetaData(ConnectorDefinition.class);
if (sinkConfig.getClassName() == null && (def == null || def.getSinkClass() == null)) {
throw new IllegalArgumentException("connector archive does not declare a sinkClass");
} Type guard
static boolean declaresSinkClass(ValidatableFunctionPackage pkg, String className) {
if (className != null) return true;
ConnectorDefinition d = pkg.getFunctionMetaData(ConnectorDefinition.class);
return d != null && d.getSinkClass() != null && !d.getSinkClass().isEmpty();
} Try / catch
try {
SinkConfigUtils.validateAndExtractDetails(cfg, sinkPkg, transformPkg, true);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Failed to extract sink class")) {
log.error("pulsar-io.yaml has no sinkClass: is this a source-only connector?", e);
}
throw e;
} Prevention
- Verify the connector type (source vs sink) matches the deployment command.
- Keep sinkClass populated in every pulsar-io.yaml you ship.
- Inspect archive YAML with 'unzip -p nar META-INF/services/pulsar-io.yaml' before deploying.
- Set className explicitly when using a custom archive to bypass descriptor resolution.
When it happens
Trigger: Uploading a connector NAR whose pulsar-io.yaml defines sourceClass but not sinkClass (a source-only connector used with 'sink create'); pulsar-io.yaml with an empty sinkClass key.
Common situations: Reusing a source connector archive as a sink; hand-edited pulsar-io.yaml missing the sinkClass line; generic connector archives where only one direction is implemented.
Related errors
- Sink package doesn't contain the META-INF/services/pulsar-io
- Invalid sink type '%s' -- Available sinks are: %s
- Could not find sink config class
- The '%s' connector does not provide a source implementation
- Class %s does not implement interface %s or %s
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/6d4d671d7db56365.
Report an issue: GitHub.