{"record":{"id":"5131cc04a6fbbe65","repo":"apache/pulsar","slug":"source-package-doesn-t-contain-the-meta-inf-servic","errorCode":null,"errorMessage":"Source package doesn't contain the META-INF/services/pulsar-io.yaml file.","messagePattern":"Source package doesn't contain the META-INF/services/pulsar-io\\.yaml file\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SourceConfigUtils.java","lineNumber":296,"sourceCode":"            if (!TopicName.isValid(sourceConfig.getLogTopic())) {\n                throw new IllegalArgumentException(\n                        String.format(\"LogTopic topic %s is invalid\", sourceConfig.getLogTopic()));\n            }\n        }\n        if (sourceConfig.getParallelism() != null && sourceConfig.getParallelism() <= 0) {\n            throw new IllegalArgumentException(\"Source parallelism must be a positive number\");\n        }\n        if (sourceConfig.getResources() != null) {\n            ResourceConfigUtils.validate(sourceConfig.getResources());\n        }\n\n        String sourceClassName = sourceConfig.getClassName();\n        // if class name in source config is not set, this should be a built-in source\n        // thus we should try to find it class name in the NAR service definition\n        if (sourceClassName == null) {\n            ConnectorDefinition connectorDefinition = sourceFunction.getFunctionMetaData(ConnectorDefinition.class);\n            if (connectorDefinition == null) {\n                throw new IllegalArgumentException(\n                        \"Source package doesn't contain the META-INF/services/pulsar-io.yaml file.\");\n            }\n            sourceClassName = connectorDefinition.getSourceClass();\n            if (sourceClassName == null) {\n                throw new IllegalArgumentException(\"Failed to extract source class from archive\");\n            }\n        }\n\n        // check if source implements the correct interfaces\n        TypeDescription sourceClass;\n        try {\n            sourceClass = sourceFunction.resolveType(sourceClassName);\n        } catch (TypePool.Resolution.NoSuchTypeException e) {\n            throw new IllegalArgumentException(\n              String.format(\"Source class %s not found in class loader\", sourceClassName), e);\n        }\n\n        if (!(sourceClass.asErasure().isAssignableTo(Source.class) || sourceClass.asErasure()","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SourceConfigUtils.java#L278-L314","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nSourceConfig cfg = new SourceConfig();\ncfg.setArchive(\"/connectors/my-source.nar\"); // className missing, NAR lacks descriptor\n// after (option A: explicit class)\ncfg.setClassName(\"com.example.MySource\");\ncfg.setArchive(\"/connectors/my-source.nar\");\n// after (option B: proper NAR)\n// rebuild NAR including src/main/resources/META-INF/services/pulsar-io.yaml with sourceClass set","handlingStrategy":"validation","validationCode":"if (cfg.getClassName() == null) {\n    try (java.util.zip.ZipFile zar = new java.util.zip.ZipFile(cfg.getArchive())) {\n        if (zar.getEntry(\"META-INF/services/pulsar-io.yaml\") == null) {\n            throw new IllegalStateException(\n                cfg.getArchive() + \" has no META-INF/services/pulsar-io.yaml; set className explicitly or rebuild the NAR\");\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    SourceConfigUtils.validateAndExtractDetails(cfg, pkg, true);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"META-INF/services/pulsar-io.yaml\")) {\n        // treat as packaging error: set className or rebuild/re-upload the NAR\n    }\n}","preventionTips":["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"],"tags":["pulsar-functions","connector-nar","missing-resource","packaging"],"backgroundTag":"missing-descriptor-file","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}