{"record":{"id":"4d077b17aceec21e","repo":"apache/skywalking","slug":"classloaderneighbor-must-not-be-null","errorCode":null,"errorMessage":"classLoaderNeighbor must not be null","messagePattern":"classLoaderNeighbor must not be null","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":368,"sourceCode":"     * @param pool                 per-file Javassist pool, typically constructed as\n     *                             {@code new ClassPool(ClassPool.getDefault())} with\n     *                             {@code LoaderClassPath(ruleLoader)} appended\n     * @param classLoaderNeighbor  a class loaded by the per-file {@code RuleClassLoader}; used\n     *                             by Javassist's {@code toClass(Class)} on Java 9+ to resolve\n     *                             the target loader. On Java 8, its classloader is passed to\n     *                             the legacy {@code toClass(ClassLoader, ProtectionDomain)}\n     */\n    public synchronized <T> void create(String metricsName,\n                                        String functionName,\n                                        ScopeType type,\n                                        Class<T> dataType,\n                                        ClassPool pool,\n                                        Class<?> classLoaderNeighbor) throws IllegalArgumentException {\n        if (pool == null) {\n            throw new IllegalArgumentException(\"pool must not be null\");\n        }\n        if (classLoaderNeighbor == null) {\n            throw new IllegalArgumentException(\"classLoaderNeighbor must not be null\");\n        }\n        createInternal(metricsName, functionName, type, dataType, pool, classLoaderNeighbor,\n            StorageManipulationOpt.withSchemaChange());\n    }\n\n    /**\n     * Remove a previously-registered metric by name. Symmetric to {@link #create(String, String,\n     * ScopeType, Class)} / the pool-aware overload. Used by runtime rule hot-remove (MAL/LAL)\n     * to retire a metric class cleanly.\n     *\n     * <p>Steps:\n     * <ol>\n     *   <li>Drops the {@link #meterPrototypes} entry so {@link #buildMetrics(String, Class)}\n     *       rejects further builds for this name.</li>\n     *   <li>Delegates to {@link MetricsStreamProcessor#removeMetric} — L1/L2 drain, worker\n     *       deregistration, shared-queue handler removal.</li>\n     *   <li>Cascades through {@link ModelRegistry#remove(Class, StorageManipulationOpt)} to drop every downsampling\n     *       variant's {@code Model} from the registry; listener {@code whenRemoving} fires for","sourceCodeStart":350,"sourceCodeEnd":386,"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#L350-L386","documentation":"The same pool-aware create overload requires a classLoaderNeighbor: a class already loaded by the per-rule RuleClassLoader. On Java 9+ Javassist's CtClass.toClass(Class) needs a class in the target loader to resolve it, and on Java 8 its classloader is used by the legacy toClass(ClassLoader, ProtectionDomain). A null neighbor therefore cannot be mapped to a defining loader and is rejected immediately.","triggerScenarios":"Calling the pool-aware create with null for the neighbor argument — e.g. an embedding that generates the neighbor class itself but skips that step, or a test double that returns null from a stubbed rule compiler.","commonSituations":"Custom integrations that drive MeterSystem directly instead of going through the MAL runtime; refactors where the code that compiled a sample class into the rule loader was reordered so create() runs before the neighbor exists.","solutions":["Compile any helper class into the rule's ClassLoader first and pass that class as the neighbor (mirror the meter-DSL runtime applier)","If custom classloaders are not needed, use the default-pool overload create(metricsName, functionName, type, dataType)"],"exampleFix":"// before\nmeterSystem.create(name, func, type, Long.class, pool, null);\n\n// after\nClass<?> neighbor = ruleClassLoader.compileUnit(\"Placeholder\"); // any class from the rule loader\nmeterSystem.create(name, func, type, Long.class, pool, neighbor);","handlingStrategy":"validation","validationCode":"if (classLoaderNeighbor == null) {\n    classLoaderNeighbor = ruleClassLoader.compileUnit(\"RuleNeighbor\"); // any class in the rule loader\n}\nmeterSystem.create(name, func, type, dataType, pool, classLoaderNeighbor);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always compile a placeholder class into the rule ClassLoader before calling create, mirroring the MAL runtime applier","Pass the simplest available rule-generated class as neighbor; any class from the target loader suffices","If you never use custom loaders, call the default-pool overload instead"],"tags":["meter-system","null-check","classloader","java-9","internal-api"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}