{"record":{"id":"f72098e4a70690cd","repo":"apache/skywalking","slug":"function-doesn-t-inherit-from-metrics","errorCode":null,"errorMessage":"Function {} doesn't inherit from Metrics.","messagePattern":"Function (.+?) doesn't inherit from Metrics\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/MeterSystem.java","lineNumber":243,"sourceCode":"                    } else {\n                        acceptance = arguments[0].getTypeName();\n                    }\n                }\n                if (foundDataType) {\n                    break;\n                }\n            }\n        }\n        if (!foundDataType) {\n            throw new IllegalArgumentException(\"Function \" + functionName\n                + \" requires <\" + acceptance + \"> in AcceptableValue\"\n                + \" but using \" + dataType.getName() + \" in the creation\");\n        }\n        final CtClass parentClass;\n        try {\n            parentClass = pool.get(meterFunction.getCanonicalName());\n            if (!Metrics.class.isAssignableFrom(meterFunction)) {\n                throw new IllegalArgumentException(\n                    \"Function \" + functionName + \" doesn't inherit from Metrics.\");\n            }\n        } catch (NotFoundException e) {\n            throw new IllegalArgumentException(\"Function \" + functionName + \" can't be found by javaassist.\");\n        }\n        final String className = formatName(metricsName);\n        // Prototype-first short-circuit (fires on runtime FILTER_ONLY re-apply). Every\n        // runtime apply hands in a fresh {@code ClassPool}, so the pool-based existence\n        // check below cannot see a Metrics class the previous apply defined in a now-dead\n        // pool. Without this guard, every FILTER_ONLY update generated a new Metrics class,\n        // new MetricsStreamProcessor workers, and a new prototype that shadowed the old\n        // one in {@link #meterPrototypes} — a removeMetric by name could only tear down the\n        // latest generation, leaving prior workers + classloaders pinned forever. Match on\n        // scope + data type + function class; any of those differing is a genuine shape\n        // change and the existing IllegalArgumentException on the pool path fires below.\n        final MeterDefinition existingDefinition = meterPrototypes.get(metricsName);\n        if (existingDefinition != null\n            && existingDefinition.getScopeType() == type","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/apache/skywalking/blob/102af09b4a56064e22050dded10e2c52e490d040/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/MeterSystem.java#L225-L261","documentation":"MeterSystem.create() generates a Metrics subclass at runtime (Javassist) for every metric declared by a MAL rule. The function class resolved from the FunctionRegister must both implement AcceptableValue<T> and inherit from org.apache.skywalking.oap.server.core.analysis.metrics.Metrics, because the generated class extends it and is pushed into the stream-processing pipeline. This IllegalArgumentException fires when the registered function class fails the Metrics.class.isAssignableFrom check.","triggerScenarios":"A MAL/LAL rule names a function whose registered implementation class implements AcceptableValue but does not extend Metrics; a custom meter function was registered into FunctionRegister with an incomplete class hierarchy (e.g. only implementing the interface); a custom function was compiled against an old SkyWalking version where the hierarchy differed.","commonSituations":"Writing a custom MAL function plugin: developers implement AcceptableValue but forget to extend a base class like AvgFunction; deploying a custom function jar built against an incompatible SkyWalking version; misregistered function name that collides with another non-Metrics entry.","solutions":["Make the custom function class extend one of the abstract bases in org.apache.skywalking.oap.server.core.analysis.meter.function (e.g. AvgFunction, SumFunction) or otherwise extend a Metrics subclass while also implementing AcceptableValue<T>","Verify the registration entry: FunctionRegister must map the MAL function name to the class that is both a Metrics and an AcceptableValue","Rebuild the custom function jar against the exact OAP server version deployed, then place it in oap-libs/ so the class hierarchy matches"],"exampleFix":"// before\npublic class MyCounter implements AcceptableValue<Long> { ... }\nFunctionRegister.register(\"mycounter\", MyCounter.class);\n\n// after\npublic class MyCounter extends CounterFunction implements AcceptableValue<Long> { ... }\n// CounterFunction already extends Metrics; the isAssignableFrom check now passes","handlingStrategy":"validation","validationCode":"// before registering/using a custom meter function\nif (!Metrics.class.isAssignableFrom(funcClass)\n        || !AcceptableValue.class.isAssignableFrom(funcClass)) {\n    throw new IllegalStateException(funcClass + \" must extend Metrics and implement AcceptableValue\");\n}\nFunctionRegister.register(functionName, funcClass);","typeGuard":"boolean isValidMeterFunction(Class<?> c) {\n    return Metrics.class.isAssignableFrom(c)\n        && AcceptableValue.class.isAssignableFrom(c);\n}","tryCatchPattern":null,"preventionTips":["Always extend an existing function base (AvgFunction, SumFunction, ...) rather than implementing AcceptableValue from scratch","Add a startup assertion that walks FunctionRegister and fails fast if any entry is not a Metrics","Build custom function jars against the exact OAP version they will be deployed into"],"tags":["meter-system","mal","javassist","custom-function","config"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}