apache/pulsar · error · IllegalArgumentException

Function package doesn't contain the META-INF/services/pulsa

Error message

Function package doesn't contain the META-INF/services/pulsar-io.yaml file.

What it means

Thrown when a sink config sets no transformFunctionClassName, so the library treats the uploaded package as a transform-function package and looks up its service definition via META-INF/services/pulsar-io.yaml. If FunctionMetaData is null the package is not a valid transform function package.

Source

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

        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 definition
            if (functionClassName == null) {
                FunctionDefinition functionDefinition =
                        transformFunction.getFunctionMetaData(FunctionDefinition.class);
                if (functionDefinition == null) {
                    throw new IllegalArgumentException(
                            "Function package doesn't contain the META-INF/services/pulsar-io.yaml file.");
                }
                functionClassName = functionDefinition.getFunctionClass();
                if (functionClassName == null) {
                    throw new IllegalArgumentException("Transform function class name must be set");
                }
            }
            TypeDefinition functionClass;
            try {
                functionClass = transformFunction.resolveType(functionClassName);
            } catch (TypePool.Resolution.NoSuchTypeException e) {
                throw new IllegalArgumentException(
                        String.format("Function class %s not found", functionClassName), e);
            }
            // extract type from transform function class
            if (!getRawFunctionTypes(functionClass, false)[1].asErasure().isAssignableTo(Record.class)) {
                throw new IllegalArgumentException("Sink transform function output must be of type Record");
            }

View on GitHub (pinned to 820761864e)

Solutions

  1. Add META-INF/services/pulsar-io.yaml declaring the FunctionDefinition (name, functionClass, type args) inside the package
  2. Or set sinkConfig.setTransformFunctionClassName(...) explicitly so the service lookup path is skipped
  3. Verify you uploaded the intended transform-function archive, not a plain sink jar

Example fix

// before
// jar has no META-INF/services/pulsar-io.yaml, transformFunctionClassName unset
sinkConfig.setTransformFunctionClassName(null);
// after
sinkConfig.setTransformFunctionClassName("org.example.MyTransformFunction");
Defensive patterns

Strategy: validation

Validate before calling

try (java.util.jar.JarFile jar = new java.util.jar.JarFile(pkgPath)) {
  if (jar.getJarEntry("META-INF/services/pulsar-io.yaml") == null
      && sinkConfig.getTransformFunctionClassName() == null) {
    throw new IllegalStateException("Package is not a transform function package and no transformFunctionClassName set");
  }
}

Try / catch

try {
  admin.sinks().createSink(config, pkgPath);
} catch (IllegalArgumentException e) {
  if (e.getMessage() != null && e.getMessage().contains("pulsar-io.yaml")) {
    log.error("Set transformFunctionClassName or fix the package's service descriptor", e);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Passing a function/transform package (or arbitrary jar) as sinkFunction while sinkConfig.getTransformFunctionClassName() is null and the archive lacks META-INF/services/pulsar-io.yaml.

Common situations: Using a plain sink jar where a transform-function package is expected; building a NAR without the service descriptor file; wrong file placed in the service directory.

Related errors


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