apache/skywalking · error · IllegalArgumentException

@MALContextFunction {}.{}() List parameters must be List<Str

Error message

@MALContextFunction {}.{}() List parameters must be List<String>, got {}

What it means

SPI validation error from MalExtensionRegistry: a @MALContextFunction method declares a List parameter whose generic element type is not String (e.g. List<Double>, List<Object>, or a raw/generic-wildcard type whose actual type argument is not exactly String.class). List<String> is the only list type the MAL argument codegen supports (StringListArgument emits Arrays.asList(new String[]{...})), so anything else is rejected at registration.

Source

Thrown at oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/v2/compiler/rt/MalExtensionRegistry.java:106

            }
            if (!SampleFamily.class.isAssignableFrom(m.getReturnType())) {
                throw new IllegalArgumentException(
                    "@MALContextFunction " + ext.getClass().getSimpleName()
                        + "." + m.getName()
                        + "() return type must be SampleFamily");
            }
            final Class<?>[] extraParamTypes = new Class<?>[paramTypes.length - 1];
            System.arraycopy(paramTypes, 1, extraParamTypes, 0, extraParamTypes.length);
            // Validate List params use List<String> (the only List type MAL supports)
            final Type[] genericTypes = m.getGenericParameterTypes();
            for (int i = 1; i < genericTypes.length; i++) {
                if (List.class.isAssignableFrom(extraParamTypes[i - 1])
                        && genericTypes[i] instanceof ParameterizedType) {
                    final Type elementType =
                        ((ParameterizedType) genericTypes[i])
                            .getActualTypeArguments()[0];
                    if (!String.class.equals(elementType)) {
                        throw new IllegalArgumentException(
                            "@MALContextFunction " + ext.getClass().getSimpleName()
                                + "." + m.getName()
                                + "() List parameters must be List<String>, got "
                                + genericTypes[i]);
                    }
                }
            }
            final String methodName = m.getName();
            if (methods.containsKey(methodName)) {
                throw new IllegalArgumentException(
                    "Duplicate @MALContextFunction name '" + methodName
                        + "' in namespace '" + namespace
                        + "' from " + ext.getClass().getName());
            }
            final String fqcn = m.getDeclaringClass().getName();
            methods.put(methodName,
                        new ExtensionMethod(fqcn, methodName, extraParamTypes));
            log.info("Registered MAL extension function {}::{}() -> {}.{}()",

View on GitHub (pinned to 102af09b4a)

Solutions

  1. Declare the parameter as exactly List<String>
  2. If numbers are needed, take a String list and parse each element inside the method (Double.parseDouble), or take a double/int scalar
  3. Rebuild the extension jar and restart OAP

Example fix

// before
@MALContextFunction
public static SampleFamily f(SampleFamily sf, List<Double> buckets) { ... }

// after
@MALContextFunction
public static SampleFamily f(SampleFamily sf, List<String> buckets) {
    List<Double> parsed = buckets.stream().map(Double::parseDouble).collect(java.util.stream.Collectors.toList());
    ...
}
Defensive patterns

Strategy: validation

Validate before calling

Type[] gpts = m.getGenericParameterTypes();
for (int i = 1; i < gpts.length; i++) {
    if (gpts[i] instanceof ParameterizedType
            && ((ParameterizedType) gpts[i]).getRawType() == List.class
            && ((ParameterizedType) gpts[i]).getActualTypeArguments()[0] != String.class) {
        throw new IllegalArgumentException("List params must be List<String>: " + m);
    }
}

Try / catch

startup-time; fix the generic signature and redeploy

Prevention

When it happens

Trigger: A method like 'f(SampleFamily, List<Double> buckets)' — the registry inspects m.getGenericParameterTypes(), finds a ParameterizedType for a List param, and the first actual type argument is not String. Note: a raw List (no generic type) slips past this check but then fails argument codegen differently — declare List<String> explicitly.

Common situations: Wanting numeric bucket lists for histogram-like extensions; using raw List from older Java code; wildcard List<? extends String> also fails the String.class.equals check.

Related errors


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