apache/skywalking · error · IllegalArgumentException

classLoaderNeighbor must not be null

Error message

classLoaderNeighbor must not be null

What it means

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.

Source

Thrown at oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/MeterSystem.java:368

     * @param pool                 per-file Javassist pool, typically constructed as
     *                             {@code new ClassPool(ClassPool.getDefault())} with
     *                             {@code LoaderClassPath(ruleLoader)} appended
     * @param classLoaderNeighbor  a class loaded by the per-file {@code RuleClassLoader}; used
     *                             by Javassist's {@code toClass(Class)} on Java 9+ to resolve
     *                             the target loader. On Java 8, its classloader is passed to
     *                             the legacy {@code toClass(ClassLoader, ProtectionDomain)}
     */
    public synchronized <T> void create(String metricsName,
                                        String functionName,
                                        ScopeType type,
                                        Class<T> dataType,
                                        ClassPool pool,
                                        Class<?> classLoaderNeighbor) throws IllegalArgumentException {
        if (pool == null) {
            throw new IllegalArgumentException("pool must not be null");
        }
        if (classLoaderNeighbor == null) {
            throw new IllegalArgumentException("classLoaderNeighbor must not be null");
        }
        createInternal(metricsName, functionName, type, dataType, pool, classLoaderNeighbor,
            StorageManipulationOpt.withSchemaChange());
    }

    /**
     * Remove a previously-registered metric by name. Symmetric to {@link #create(String, String,
     * ScopeType, Class)} / the pool-aware overload. Used by runtime rule hot-remove (MAL/LAL)
     * to retire a metric class cleanly.
     *
     * <p>Steps:
     * <ol>
     *   <li>Drops the {@link #meterPrototypes} entry so {@link #buildMetrics(String, Class)}
     *       rejects further builds for this name.</li>
     *   <li>Delegates to {@link MetricsStreamProcessor#removeMetric} — L1/L2 drain, worker
     *       deregistration, shared-queue handler removal.</li>
     *   <li>Cascades through {@link ModelRegistry#remove(Class, StorageManipulationOpt)} to drop every downsampling
     *       variant's {@code Model} from the registry; listener {@code whenRemoving} fires for

View on GitHub (pinned to 102af09b4a)

Solutions

  1. Compile any helper class into the rule's ClassLoader first and pass that class as the neighbor (mirror the meter-DSL runtime applier)
  2. If custom classloaders are not needed, use the default-pool overload create(metricsName, functionName, type, dataType)

Example fix

// before
meterSystem.create(name, func, type, Long.class, pool, null);

// after
Class<?> neighbor = ruleClassLoader.compileUnit("Placeholder"); // any class from the rule loader
meterSystem.create(name, func, type, Long.class, pool, neighbor);
Defensive patterns

Strategy: validation

Validate before calling

if (classLoaderNeighbor == null) {
    classLoaderNeighbor = ruleClassLoader.compileUnit("RuleNeighbor"); // any class in the rule loader
}
meterSystem.create(name, func, type, dataType, pool, classLoaderNeighbor);

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of apache/skywalking@102af09b4a (2026-08-14). Data as JSON: /api/errors/4d077b17aceec21e. Report an issue: GitHub.