{"record":{"id":"4336de409c0277e1","repo":"apache/skywalking","slug":"function-can-t-be-found-by-javaassist","errorCode":null,"errorMessage":"Function {} can't be found by javaassist.","messagePattern":"Function (.+?) can't be found by javaassist\\.","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":247,"sourceCode":"                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\n            && existingDefinition.getDataType().equals(dataType)\n            && existingDefinition.getMeterPrototype().getClass().getSuperclass() == meterFunction) {\n            log.debug(\"Metric {} already registered with matching shape; reusing existing \"\n                + \"Metrics class + workers (FILTER_ONLY re-apply path).\", metricsName);","sourceCodeStart":229,"sourceCodeEnd":265,"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#L229-L265","documentation":"During metric creation MeterSystem resolves the function class in the Javassist ClassPool via pool.get(meterFunction.getCanonicalName()) to obtain the CtClass parent for the generated Metrics subclass. A javassist.NotFoundException means the pool's classpath does not contain the function class, even though the JVM classloader can see it. It is rethrown as this IllegalArgumentException with the 'can't be found by javaassist' wording.","triggerScenarios":"A custom meter function jar sits on the JVM classpath but was never appended to the ClassPool with pool.appendClassPath / ClassPool.getDefault().appendClassPath; the pool-aware create(...) overload received a fresh pool built for a runtime rule that only includes the rule classloader's path, not the jar holding the function; OAP runs from a shaded/fat-jar layout where the function class lives in a nested jar the default pool cannot read.","commonSituations":"Deploying custom MAL function plugins into oap-libs without ensuring the meter-DSL runtime adds that path to its ClassPool; running OAP as a fat jar (java -jar) where ClassPool.getDefault() cannot resolve classes inside BOOT-INF; application-server classloader setups that hide the jar from Javassist's default ClassClassPath.","solutions":["Append the missing path to the pool before create(): ClassPool.getDefault().appendClassPath(new ClassClassPath(meterFunctionClass)) or append a JarClassPath for the plugin jar","If running as a fat/shaded jar, ensure the function classes are unpacked or add a LoaderClassPath for the container classloader","Verify the function jar is actually in oap-libs/ (or on the OAP classpath) and was loaded by the same loader that supplied the class to FunctionRegister"],"exampleFix":"// before\nClassPool pool = ClassPool.getDefault();\nmeterSystem.create(name, func, type, Long.class, pool, neighbor);\n// -> NotFoundException: Function ... can't be found by javaassist.\n\n// after\nClassPool pool = ClassPool.getDefault();\npool.appendClassPath(new org.javassist.ClassClassPath(functionClass));\nmeterSystem.create(name, func, type, Long.class, pool, neighbor);","handlingStrategy":"validation","validationCode":"ClassPool pool = ClassPool.getDefault();\ntry {\n    pool.get(meterFunctionClass.getCanonicalName());\n} catch (javassist.NotFoundException e) {\n    pool.appendClassPath(new org.javassist.ClassClassPath(meterFunctionClass));\n}\nmeterSystem.create(name, functionName, type, dataType, pool, neighbor);","typeGuard":null,"tryCatchPattern":"try {\n    meterSystem.create(...);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"can't be found by javaassist\")) {\n        pool.appendClassPath(new ClassClassPath(functionClass));\n        meterSystem.create(...); // retry once with fixed classpath\n    } else { throw e; }\n}","preventionTips":["Append a ClassClassPath for every custom function class right after building any new ClassPool","In fat-jar deployments, append a LoaderClassPath for the application classloader to the default pool","Keep custom function jars in oap-libs so both the JVM loader and Javassist pools see them"],"tags":["meter-system","javassist","classpath","classloader","custom-function"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}