{"record":{"id":"f86c758b749c922a","repo":"apache/pulsar","slug":"function-runtime-customizer-s-must-implement-kube","errorCode":null,"errorMessage":"Function runtime customizer %s must implement KubernetesManifestCustomizer","messagePattern":"Function runtime customizer (.+?) must implement KubernetesManifestCustomizer","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/runtime/kubernetes/KubernetesRuntimeFactory.java","lineNumber":232,"sourceCode":"        this.functionInstanceMinResources = workerConfig.getFunctionInstanceMinResources();\n        this.functionInstanceMaxResources = workerConfig.getFunctionInstanceMaxResources();\n        this.functionInstanceResourceGranularities = workerConfig.getFunctionInstanceResourceGranularities();\n        this.functionInstanceResourceChangeInLockStep = workerConfig.isFunctionInstanceResourceChangeInLockStep();\n        this.secretsProviderConfigurator = secretsProviderConfigurator;\n        this.authenticationEnabled = workerConfig.isAuthenticationEnabled();\n        this.javaInstanceJarFile = this.pulsarRootDir + \"/instances/java-instance.jar\";\n        this.pythonInstanceFile = this.pulsarRootDir + \"/instances/python-instance/python_instance_main.py\";\n        this.serverCaBytes = workerConfig.getTlsTrustChainBytes();\n        try {\n            setupClient();\n        } catch (Exception e) {\n            log.error().exception(e).log(\"Failed to setup client\");\n            throw new RuntimeException(e);\n        }\n        // make sure the provided class is a kubernetes auth provider, this needs to run before the authProvider!\n        if (runtimeCustomizer.isPresent()) {\n            if (!(runtimeCustomizer.get() instanceof KubernetesManifestCustomizer)) {\n                throw new IllegalArgumentException(\"Function runtime customizer \"\n                        + runtimeCustomizer.get().getClass().getName()\n                        + \" must implement KubernetesManifestCustomizer\");\n            } else {\n                KubernetesManifestCustomizer manifestCustomizer =\n                        (KubernetesManifestCustomizer) runtimeCustomizer.get();\n                this.manifestCustomizer = Optional.of(manifestCustomizer);\n            }\n        } else {\n            this.manifestCustomizer = Optional.empty();\n        }\n\n        // make sure the provided class is a kubernetes auth provider\n        if (functionAuthProvider.isPresent()) {\n            if (!(functionAuthProvider.get() instanceof KubernetesFunctionAuthProvider)) {\n                throw new IllegalArgumentException(\"Function authentication provider \"\n                        + functionAuthProvider.get().getClass().getName()\n                        + \" must implement KubernetesFunctionAuthProvider\");\n            } else {","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/runtime/kubernetes/KubernetesRuntimeFactory.java#L214-L250","documentation":"KubernetesRuntimeFactory.initialize validates that the configured function runtime customizer class implements KubernetesManifestCustomizer before using it. If a class was configured via functionsWorkerCustomNarWindowTitle/customizer property but does not implement the required interface, initialization fails fast with IllegalArgumentException.","triggerScenarios":"Setting the runtime customizer class name (functionRuntimeCustomizer config in functions_worker.yml) to a class that exists but implements a different customizer interface (e.g. a generic RuntimeCustomizer or wrong plugin class), causing the instanceof KubernetesManifestCustomizer check at line 232 to fail.","commonSituations":"Copy-pasting a customizer class name from another runtime (e.g. process/thread runtime customizer); upgrading Pulsar where the customizer interface was renamed/split; typos leading to a similarly-named wrong class; custom NAR containing an unrelated class.","solutions":["Make the configured class implement org.apache.pulsar.functions.runtime.kubernetes.KubernetesManifestCustomizer.","Verify the class name in functions_worker.yml points to the Kubernetes manifest customizer, not another runtime's customizer.","Check the Pulsar version docs: ensure the interface name matches the version deployed (interface has changed across releases).","Rebuild/redeploy the custom NAR containing the corrected class."],"exampleFix":"// before\npublic class MyCustomizer implements RuntimeCustomizer { ... }\n// after\npublic class MyCustomizer implements KubernetesManifestCustomizer { ... }","handlingStrategy":"validation","validationCode":"Class<?> c = Class.forName(configuredCustomizerClassName);\nif (!KubernetesManifestCustomizer.class.isAssignableFrom(c)) {\n    throw new IllegalArgumentException(configuredCustomizerClassName + \" must implement KubernetesManifestCustomizer\");\n}","typeGuard":"boolean isValidCustomizer(Object o) { return o instanceof KubernetesManifestCustomizer; }","tryCatchPattern":"try { factory.initialize(...); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"KubernetesManifestCustomizer\")) { log.fatal(\"Bad customizer config\"); } throw e; }","preventionTips":["Verify the configured class implements KubernetesManifestCustomizer, not a generic RuntimeCustomizer","Recompile custom NARs against the deployed Pulsar version","Smoke-test worker startup in CI after changing functions_worker.yml"],"tags":["kubernetes","configuration","type-mismatch","pulsar-functions"],"backgroundTag":"class-does-not-implement-interface","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}