{"record":{"id":"ef52752e27f08fe3","repo":"apache/skywalking","slug":"malcontextfunction-first-parameter-must-b","errorCode":null,"errorMessage":"@MALContextFunction {}.{}() first parameter must be SampleFamily","messagePattern":"@MALContextFunction (.+?)\\.(.+?)\\(\\) first parameter must be SampleFamily","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/rt/MalExtensionRegistry.java","lineNumber":84,"sourceCode":"        if (namespace == null || namespace.isEmpty()) {\n            log.warn(\"Skipping MalFunctionExtension with null/empty name: {}\",\n                     ext.getClass().getName());\n            return;\n        }\n        final Map<String, ExtensionMethod> methods = new HashMap<>();\n        for (final Method m : ext.getClass().getMethods()) {\n            if (!m.isAnnotationPresent(MALContextFunction.class)) {\n                continue;\n            }\n            if (!Modifier.isStatic(m.getModifiers())) {\n                throw new IllegalArgumentException(\n                    \"@MALContextFunction \" + ext.getClass().getSimpleName()\n                        + \".\" + m.getName() + \"() must be static\");\n            }\n            final Class<?>[] paramTypes = m.getParameterTypes();\n            if (paramTypes.length == 0\n                    || !SampleFamily.class.isAssignableFrom(paramTypes[0])) {\n                throw new IllegalArgumentException(\n                    \"@MALContextFunction \" + ext.getClass().getSimpleName()\n                        + \".\" + m.getName()\n                        + \"() first parameter must be SampleFamily\");\n            }\n            if (!SampleFamily.class.isAssignableFrom(m.getReturnType())) {\n                throw new IllegalArgumentException(\n                    \"@MALContextFunction \" + ext.getClass().getSimpleName()\n                        + \".\" + m.getName()\n                        + \"() return type must be SampleFamily\");\n            }\n            final Class<?>[] extraParamTypes = new Class<?>[paramTypes.length - 1];\n            System.arraycopy(paramTypes, 1, extraParamTypes, 0, extraParamTypes.length);\n            // Validate List params use List<String> (the only List type MAL supports)\n            final Type[] genericTypes = m.getGenericParameterTypes();\n            for (int i = 1; i < genericTypes.length; i++) {\n                if (List.class.isAssignableFrom(extraParamTypes[i - 1])\n                        && genericTypes[i] instanceof ParameterizedType) {\n                    final Type elementType =","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/apache/skywalking/blob/102af09b4a56064e22050dded10e2c52e490d040/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/v2/compiler/rt/MalExtensionRegistry.java#L66-L102","documentation":"SPI validation error from MalExtensionRegistry: a @MALContextFunction method's first parameter is missing or is not assignable to SampleFamily. The first parameter is the implicit receiver — the codegen passes the current chain value (a SampleFamily) as the first argument of the generated static call, so the signature must start with SampleFamily (or a supertype of it).","triggerScenarios":"Methods like 'f()' (zero params) or 'f(String key, SampleFamily sf)' / 'f(double factor)' where SampleFamily is not paramTypes[0]. The check requires paramTypes.length > 0 and SampleFamily.class.isAssignableFrom(paramTypes[0]).","commonSituations":"Porting a plain utility method and adding the annotation without reordering parameters; assuming the metric is passed some other way (return value, ThreadLocal); copy-paste from a non-MAL helper class.","solutions":["Make SampleFamily the first parameter: public static SampleFamily f(SampleFamily sf, <extra params...>)","Any extra configuration parameters come after the SampleFamily one","Rebuild and redeploy the extension jar, then restart OAP"],"exampleFix":"// before\n@MALContextFunction\npublic static SampleFamily f(double factor) { ... }\n\n// after\n@MALContextFunction\npublic static SampleFamily f(SampleFamily sf, double factor) { ... }","handlingStrategy":"validation","validationCode":"Class<?>[] pts = m.getParameterTypes();\nif (pts.length == 0 || !SampleFamily.class.isAssignableFrom(pts[0])) {\n    throw new IllegalArgumentException(\"First param must be SampleFamily: \" + m);\n}","typeGuard":null,"tryCatchPattern":"startup-time; fail the extension's own unit test instead of catching at runtime","preventionTips":["SampleFamily always first, configuration params after","Cover every extension with a registry-init test in CI"],"tags":["mal","extension","spi","signature","startup"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}