{"record":{"id":"4d4e833536a75361","repo":"apache/skywalking","slug":"unknown-mal-extension-function","errorCode":null,"errorMessage":"Unknown MAL extension function: {}::{}","messagePattern":"Unknown MAL extension function: (.+?)::(.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/v2/compiler/MALMethodChainCodegen.java","lineNumber":151,"sourceCode":"    }\n\n    // ==================== Extension function codegen ====================\n\n    /**\n     * Emits a direct static method call for an extension function.\n     *\n     * <p>For {@code .test::scale(3.0)} on variable {@code _metric}, generates:\n     * {@code _metric = TestMalExtension.scale(_metric, 3.0);}\n     */\n    private void emitExtensionCall(final StringBuilder sb,\n                                    final String var,\n                                    final MALExpressionModel.MethodCall mc) {\n        final String ns = mc.getNamespace();\n        final String method = mc.getName();\n        final MalExtensionRegistry.ExtensionMethod em =\n            MalExtensionRegistry.lookup(ns, method);\n        if (em == null) {\n            throw new IllegalArgumentException(\n                \"Unknown MAL extension function: \" + ns + \"::\" + method);\n        }\n        final List<MALExpressionModel.Argument> args = mc.getArguments();\n        final int expectedArgs = em.getExtraParamTypes().length;\n        if (args.size() != expectedArgs) {\n            throw new IllegalArgumentException(\n                \"MAL extension \" + ns + \"::\" + method + \" expects \"\n                    + expectedArgs + \" argument(s), got \" + args.size());\n        }\n        sb.append(\"  \").append(var).append(\" = \")\n          .append(em.getDeclaringClass()).append('.')\n          .append(em.getMethodName()).append('(')\n          .append(var);\n        for (int i = 0; i < args.size(); i++) {\n            sb.append(\", \");\n            generateExtensionArg(sb, args.get(i), em.getExtraParamTypes()[i]);\n        }\n        sb.append(\");\\n\");","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/apache/skywalking/blob/102af09b4a56064e22050dded10e2c52e490d040/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/v2/compiler/MALMethodChainCodegen.java#L133-L169","documentation":"Thrown by MALMethodChainCodegen when an expression uses the extension-call syntax namespace::method() but MalExtensionRegistry.lookup(namespace, method) returns null — i.e. no SPI-registered MalFunctionExtension provides that method under that namespace. The v2 compiler resolves extension calls at compile time and emits direct static calls, so an unresolvable name fails the whole rule compilation with IllegalArgumentException.","triggerScenarios":"Expression like 'metric.sum([\\'svc\\']).myext::scale(3.0)' where (a) no MalFunctionExtension implementation named 'myext' is on the ServiceLoader path, (b) the method exists but under a different namespace, (c) the namespace is registered but the method name is misspelled, or (d) the SPI file META-INF/services/org.apache.skywalking.oap.meter.analyzer.v2.spi.MalFunctionExtension is missing the implementing class.","commonSituations":"Typoing the namespace or method name in the YAML rule; deploying a custom extension jar that is not on the OAP classpath (not copied into oap-libs or the ext directory); upgrading OAP where the extension class/package was renamed; forgetting the SPI registration file in a newly built extension jar.","solutions":["Verify the namespace and method name in the expression exactly match ext.name() and the @MALContextFunction-annotated static method of your extension class","Confirm the extension jar is on the OAP classpath and contains META-INF/services/org.apache.skywalking.oap.meter.analyzer.v2.spi.MalFunctionExtension listing the implementation class","Check the startup log for 'Registered MAL extension function {ns}::{method}()' lines — if the namespace is absent, SPI discovery failed","Remove the extension call from the rule if the extension was never intended to ship"],"exampleFix":"# before (rule YAML)\nexpr: node_cpu.sum(['mode']).genai::estimateCost()\n# extension class declares name() = \"genAi\" (case mismatch)\n\n# after — align names\npublic String name() { return \"genai\"; }\n# or fix the rule\nexpr: node_cpu.sum(['mode']).genAi::estimateCost()","handlingStrategy":"validation","validationCode":"MalExtensionRegistry.ExtensionMethod em = MalExtensionRegistry.lookup(ns, method);\nif (em == null) {\n    throw new IllegalArgumentException(\n        \"Extension \" + ns + \"::\" + method + \" not registered; available: \"\n        + MalExtensionRegistry.namespaces());\n}","typeGuard":null,"tryCatchPattern":"catch IllegalArgumentException around DSL.parse; surface ns::method in the error and check the startup log for 'Registered MAL extension function' lines","preventionTips":["Grep OAP startup logs for 'Registered MAL extension function' to confirm the namespace loaded before relying on it","Pin extension jar versions in deployment so namespace availability matches rule files","Add a smoke-test rule using each extension function to your CI rule-validation test"],"tags":["mal","spi","extension","classpath","configuration"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}