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

  1. Verify the NAR contains META-INF/services/org.apache.pulsar.io.spi.Loadable and that it declares a class implementing PulsarSource/PulsarSink
  2. Rebuild the connector with a matching version of pulsar-io-core and the pulsar-io-spi dependencies (scope provided)
  3. Re-upload/re-download the archive and retry registration to rule out a corrupted transfer
  4. 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

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


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