{"record":{"id":"c7427b3e5a58f2a2","repo":"apache/skywalking","slug":"pool-must-not-be-null","errorCode":null,"errorMessage":"pool must not be null","messagePattern":"pool 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":365,"sourceCode":"     * @param functionName         function provided through {@link MeterFunction}\n     * @param type                 scope type\n     * @param dataType             accepted value data type\n     * @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","sourceCodeStart":347,"sourceCodeEnd":383,"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#L347-L383","documentation":"The pool-aware MeterSystem.create(String, String, ScopeType, Class, ClassPool, Class) overload is used by the runtime MAL/LAL rule applier, which builds a fresh ClassPool and a per-file RuleClassLoader for each rule file. It rejects a null pool up front because every subsequent step (resolving the function CtClass, generating and toClass()-ing the Metrics subclass) dereferences it. This is a fail-fast guard against internal API misuse, not a user-config error.","triggerScenarios":"Calling the pool-aware create overload with a null ClassPool — typically a custom embedding of MeterSystem, a unit test stubbing the rule applier, or a code path that builds a pool lazily and passes the unassigned field.","commonSituations":"Third-party code embedding the OAP core MeterSystem outside the standard server-starter; test harnesses that pass null intending the default-pool overload to be selected, but matching the pool overload because of an extra argument; refactors that removed pool construction from a branch.","solutions":["Construct and pass a real pool: ClassPool pool = ClassPool.getDefault(); pool.appendClassPath(...) for the rule's loader — see the runtime applier in oap-server/analyzer for the canonical pattern","If you do not need a custom loader, call the simpler overload create(metricsName, functionName, type, dataType) which uses the default pool"],"exampleFix":"// before\nmeterSystem.create(name, func, type, Long.class, null, neighbor);\n\n// after\nClassPool pool = ClassPool.getDefault();\npool.appendClassPath(new ClassClassPath(functionClass));\nmeterSystem.create(name, func, type, Long.class, pool, neighbor);","handlingStrategy":"validation","validationCode":"ClassPool pool = (pool == null) ? ClassPool.getDefault() : pool;\n// or simply reject before calling:\n// Objects.requireNonNull(pool, \"pool\");\nmeterSystem.create(name, func, type, dataType, pool, neighbor);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default to ClassPool.getDefault() when no isolation is needed","In tests, build the pool in @BeforeEach so it can never be null at the call site","Reserve the pool-aware overload for callers that actually manage a rule ClassLoader"],"tags":["meter-system","null-check","internal-api","javassist"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}