apache/pulsar · error · IllegalArgumentException
Source package doesn't contain the META-INF/services/pulsar-
Error message
Source package doesn't contain the META-INF/services/pulsar-io.yaml file.
What it means
When SourceConfig.className is null, the library treats the package as a built-in (NAR) connector and tries to read its connector metadata from META-INF/services/pulsar-io.yaml inside the archive. If that descriptor is missing, the connector definition is null and this IllegalArgumentException is thrown — the package is neither a plain-class source nor a well-formed NAR connector.
Source
Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SourceConfigUtils.java:296
if (!TopicName.isValid(sourceConfig.getLogTopic())) {
throw new IllegalArgumentException(
String.format("LogTopic topic %s is invalid", sourceConfig.getLogTopic()));
}
}
if (sourceConfig.getParallelism() != null && sourceConfig.getParallelism() <= 0) {
throw new IllegalArgumentException("Source parallelism must be a positive number");
}
if (sourceConfig.getResources() != null) {
ResourceConfigUtils.validate(sourceConfig.getResources());
}
String sourceClassName = sourceConfig.getClassName();
// if class name in source config is not set, this should be a built-in source
// thus we should try to find it class name in the NAR service definition
if (sourceClassName == null) {
ConnectorDefinition connectorDefinition = sourceFunction.getFunctionMetaData(ConnectorDefinition.class);
if (connectorDefinition == null) {
throw new IllegalArgumentException(
"Source package doesn't contain the META-INF/services/pulsar-io.yaml file.");
}
sourceClassName = connectorDefinition.getSourceClass();
if (sourceClassName == null) {
throw new IllegalArgumentException("Failed to extract source class from archive");
}
}
// check if source implements the correct interfaces
TypeDescription sourceClass;
try {
sourceClass = sourceFunction.resolveType(sourceClassName);
} catch (TypePool.Resolution.NoSuchTypeException e) {
throw new IllegalArgumentException(
String.format("Source class %s not found in class loader", sourceClassName), e);
}
if (!(sourceClass.asErasure().isAssignableTo(Source.class) || sourceClass.asErasure()View on GitHub (pinned to 820761864e)
Solutions
- Set sourceConfig.setClassName("com.example.MySource") explicitly so the archive metadata is not needed
- If it should be a NAR connector, rebuild the NAR so META-INF/services/pulsar-io.yaml (with the sourceClass entry) is packaged in the archive
- Verify the uploaded archive: unzip -l my-source.nar | grep pulsar-io.yaml and re-upload the correct file
- Check the YAML has a sourceClass key pointing at the Source implementation
Example fix
// before
SourceConfig cfg = new SourceConfig();
cfg.setArchive("/connectors/my-source.nar"); // className missing, NAR lacks descriptor
// after (option A: explicit class)
cfg.setClassName("com.example.MySource");
cfg.setArchive("/connectors/my-source.nar");
// after (option B: proper NAR)
// rebuild NAR including src/main/resources/META-INF/services/pulsar-io.yaml with sourceClass set Defensive patterns
Strategy: validation
Validate before calling
if (cfg.getClassName() == null) {
try (java.util.zip.ZipFile zar = new java.util.zip.ZipFile(cfg.getArchive())) {
if (zar.getEntry("META-INF/services/pulsar-io.yaml") == null) {
throw new IllegalStateException(
cfg.getArchive() + " has no META-INF/services/pulsar-io.yaml; set className explicitly or rebuild the NAR");
}
}
} Try / catch
try {
SourceConfigUtils.validateAndExtractDetails(cfg, pkg, true);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().contains("META-INF/services/pulsar-io.yaml")) {
// treat as packaging error: set className or rebuild/re-upload the NAR
}
} Prevention
- For custom connectors, always include src/main/resources/META-INF/services/pulsar-io.yaml in the NAR build
- Set className explicitly when using plain JARs instead of NARs
- Verify archives with 'unzip -l ... | grep pulsar-io.yaml' before upload in CI
When it happens
Trigger: Registering a source with archive set but className unset, where the uploaded NAR/JAR lacks META-INF/services/pulsar-io.yaml — e.g. a hand-assembled NAR, a jar built without the pulsar-io.yaml resource, or pointing at the wrong archive file.
Common situations: Building a custom connector NAR without including the service descriptor resource; using a fat jar of connector code rather than a proper NAR package; typos in the archive path so a wrong file gets uploaded; upgrading a connector to a build that no longer bundles the descriptor.
Related errors
- Failed to extract source class from archive
- Source class %s not found in class loader
- Function package doesn't contain the META-INF/services/pulsa
- You must specify either a Fully Qualified Function Name (FQF
- You must specify a name for the function or a Fully Qualifie
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/5131cc04a6fbbe65.
Report an issue: GitHub.