apache/pulsar · error · IllegalArgumentException
Failed to extract %s class from archive
Error message
Failed to extract %s class from archive
What it means
Thrown by getClassLoaderFromPackage when a NAR archive is being registered as a Pulsar IO connector source/sink. After loading the NAR, the code calls ConnectorUtils.getIOSourceClass/getIOSinkClass, which reads the connector metadata; if that I/O fails, the code wraps the IOException in an IllegalArgumentException naming the component type (source/sink).
Source
Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionRuntimeCommon.java:92
if (isEmpty(connectorClassName)) {
if (narClassLoader == null) {
throw new IllegalArgumentException(String.format("%s package does not have the correct format. "
+ "Pulsar cannot determine if the package is a NAR package or JAR package. "
+ "%s classname is not provided and attempts to load it as a NAR package produced "
+ "the following error.",
FunctionCommon.capFirstLetter(componentType), FunctionCommon.capFirstLetter(componentType)),
narClassLoaderException);
}
try {
if (componentType == FunctionDetails.ComponentType.FUNCTION) {
connectorClassName = FunctionUtils.getFunctionClass(narClassLoader);
} else if (componentType == FunctionDetails.ComponentType.SOURCE) {
connectorClassName = ConnectorUtils.getIOSourceClass(narClassLoader);
} else {
connectorClassName = ConnectorUtils.getIOSinkClass(narClassLoader);
}
} catch (IOException e) {
throw new IllegalArgumentException(String.format("Failed to extract %s class from archive",
componentType.toString().toLowerCase()), e);
}
try {
narClassLoader.loadClass(connectorClassName);
keepNarClassLoader = true;
return narClassLoader;
} catch (ClassNotFoundException | NoClassDefFoundError e) {
throw new IllegalArgumentException(String.format("%s class %s must be in class path",
FunctionCommon.capFirstLetter(componentType), connectorClassName), e);
}
} else {
// if connector class name is provided, we need to try to load it as a JAR and as a NAR.
if (jarClassLoader != null) {
try {
jarClassLoader.loadClass(connectorClassName);
keepJarClassLoader = true;View on GitHub (pinned to 820761864e)
Solutions
- Verify the NAR contains META-INF/services/org.apache.pulsar.io.spi.Loadable and that it declares a class implementing PulsarSource/PulsarSink
- Rebuild the connector with a matching version of pulsar-io-core and the pulsar-io-spi dependencies (scope provided)
- Re-upload/re-download the archive and retry registration to rule out a corrupted transfer
- Unzip the NAR locally and confirm the descriptor is present and well-formed before uploading
Example fix
// before: NAR built as plain jar without IO SPI descriptor
// pom.xml missing pulsar-io-core
// after
<dependency>
<groupId>org.apache.pulsar</groupId>
<artifactId>pulsar-io-core</artifactId>
<version>${pulsar.version}</version>
<scope>provided</scope>
</dependency> Defensive patterns
Strategy: validation
Validate before calling
try (JarFile jar = new JarFile(archivePath)) {
if (jar.getEntry("META-INF/services/org.apache.pulsar.io.spi.Loadable") == null) {
throw new IllegalStateException("Archive is missing connector SPI descriptor");
}
} Try / catch
try {
createFunction(...);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Failed to extract")) {
log.error("NAR missing/malformed connector descriptor", e);
}
} Prevention
- Always build NARs with the pulsar NAR packaging so META-INF/services descriptors are generated
- Verify archive contents locally with 'unzip -l' before uploading
- Keep pulsar-io-core at 'provided' scope so SPI versions match the broker
When it happens
Trigger: Registering/updating a source or sink function whose archive is a NAR package; ConnectorUtils.getIOSourceClass or getIOSinkClass throws IOException while reading the NAR's META-INF/services connector descriptor (missing, unreadable, or malformed descriptor inside the archive).
Common situations: A NAR built without the META-INF/services/org.apache.pulsar.io.spi.Loadable file; archive corrupted during upload; the NAR was built for a different Pulsar IO SPI version so the descriptor cannot be read.
Related errors
- %s class %s must be in class path
- Config class not found: %s
- Timestamp extractor class %s must be in class path
- Loading '' failed due to
- Classloading is not enabled
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/7d9a93754f99c072.
Report an issue: GitHub.