{"record":{"id":"27f0f7581e5e862a","repo":"apache/skywalking","slug":"malcontextfunction-return-type-must-be-sa","errorCode":null,"errorMessage":"@MALContextFunction {}.{}() return type must be SampleFamily","messagePattern":"@MALContextFunction (.+?)\\.(.+?)\\(\\) return type 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":90,"sourceCode":"        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 =\n                        ((ParameterizedType) genericTypes[i])\n                            .getActualTypeArguments()[0];\n                    if (!String.class.equals(elementType)) {\n                        throw new IllegalArgumentException(\n                            \"@MALContextFunction \" + ext.getClass().getSimpleName()\n                                + \".\" + m.getName()","sourceCodeStart":72,"sourceCodeEnd":108,"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#L72-L108","documentation":"SPI validation error from MalExtensionRegistry: a @MALContextFunction method's return type is not assignable to SampleFamily. Because every extension call is a chain step (metric.ns::method(...).nextStep(...)), the generated code reassigns the chain variable to the call's result, which must therefore be a SampleFamily.","triggerScenarios":"Methods returning void, boolean, double, String, or any non-SampleFamily type while carrying @MALContextFunction. The check SampleFamily.class.isAssignableFrom(m.getReturnType()) fails at registry init during OAP startup.","commonSituations":"Annotating predicate helpers (boolean) or value extractors (double) that were meant for use inside closures, not as chain steps; refactoring a chain method to return an intermediate internal type.","solutions":["Change the method to accept and return SampleFamily (wrap values into the SampleFamily if needed)","If the helper is not a chain step, remove the @MALContextFunction annotation and use it only from generated closure code or Java","Rebuild the extension jar and restart OAP"],"exampleFix":"// before\n@MALContextFunction\npublic static boolean isHot(SampleFamily sf) { ... }\n\n// after\n@MALContextFunction\npublic static SampleFamily hotOnly(SampleFamily sf) { return sf.filter(...); }\n// or, if not a chain step: drop the annotation","handlingStrategy":"validation","validationCode":"if (!SampleFamily.class.isAssignableFrom(m.getReturnType())) {\n    throw new IllegalArgumentException(\"Return type must be SampleFamily: \" + m);\n}","typeGuard":null,"tryCatchPattern":"startup-time; fix the extension and redeploy","preventionTips":["Chain steps return SampleFamily; helpers that don't should not carry the annotation","Review annotations whenever refactoring return types"],"tags":["mal","extension","spi","signature","startup"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}