{"record":{"id":"c316e9594daff487","repo":"apache/skywalking","slug":"malcontextfunction-must-be-static","errorCode":null,"errorMessage":"@MALContextFunction {}.{}() must be static","messagePattern":"@MALContextFunction (.+?)\\.(.+?)\\(\\) must be static","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":77,"sourceCode":"                ServiceLoader.load(MalFunctionExtension.class)) {\n            register(ext);\n        }\n    }\n\n    private static void register(final MalFunctionExtension ext) {\n        final String namespace = ext.name();\n        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];","sourceCodeStart":59,"sourceCodeEnd":95,"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#L59-L95","documentation":"Startup-time SPI validation error from MalExtensionRegistry: a class implementing MalFunctionExtension declares a method annotated @MALContextFunction that is not static. The v2 compiler generates direct static calls (e.g. com.example.MyExtension.myMethod(sf, args)) with no receiver object, so non-static methods cannot be supported and registration aborts with IllegalArgumentException during ServiceLoader scanning at OAP boot.","triggerScenarios":"Writing an extension method as an instance method: 'public SampleFamily transform(SampleFamily sf, double f)' with @MALContextFunction but without 'static'. The check Modifier.isStatic(m.getModifiers()) fails for the annotated method.","commonSituations":"First-time extension authors copying instance-style helper methods into the extension class; IDE auto-generating an instance method; refactoring that accidentally drops the static modifier.","solutions":["Add 'static' to every @MALContextFunction method","If the method needs instance state, move that state to static fields or configuration and keep the method static","Rebuild the extension jar; restart OAP and confirm the 'Registered MAL extension function' log line appears"],"exampleFix":"// before\n@MALContextFunction\npublic SampleFamily transform(SampleFamily sf, double factor) { ... }\n\n// after\n@MALContextFunction\npublic static SampleFamily transform(SampleFamily sf, double factor) { ... }","handlingStrategy":"validation","validationCode":"for (Method m : ext.getClass().getMethods()) {\n    if (m.isAnnotationPresent(MALContextFunction.class)\n            && !Modifier.isStatic(m.getModifiers())) {\n        throw new IllegalArgumentException(\"Non-static @MALContextFunction: \" + m);\n    }\n}","typeGuard":null,"tryCatchPattern":"raised at OAP startup during SPI scan; catch at bootstrap is not useful — fix the extension and redeploy","preventionTips":["Copy the canonical extension example from the meter-analyzer docs","Add an extension unit test that boots MalExtensionRegistry.init() so signature errors fail the build, not production"],"tags":["mal","extension","spi","static","startup"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}