apache/skywalking · error · IllegalArgumentException

tag() requires exactly one string literal argument, e.g. tag

Error message

tag() requires exactly one string literal argument, e.g. tag("KEY")

What it means

Thrown by MeterSystem.createInternal when the requested function exists but its AcceptableValue generic parameter does not equal the data type passed by the caller (e.g. the function is AcceptableValue<Long> and the creation path supplies Double). The meter framework generates a Javassist subclass binding the function to the metric's data type; mismatched generics cannot be compiled safely, so it fails fast with the expected vs. actual type names.

Source

Thrown at oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/v2/compiler/LALValueCodegen.java:742

            generateBinaryExpression(sb, value.getConcatParts(),
                value.getConcatOps(), genCtx);
            return;
        }

        // Handle parenthesized expression: (innerExpr as Type).chain...
        if (value.getParenInner() != null) {
            generateParenAccess(sb, value, genCtx);
            return;
        }

        // tag("KEY") — reads LogData tags via LalRuntimeHelper.tagValue().
        // Only valid when input is LogData.Builder (i.e. no inputType, or inputType
        // is LogData.Builder).  For typed inputs, tag() is not supported — use
        // parsed.* to access fields on the typed input.
        if ("tag".equals(value.getFunctionCallName())) {
            if (value.getFunctionCallArgs().size() != 1
                    || !value.getFunctionCallArgs().get(0).getValue().isStringLiteral()) {
                throw new IllegalArgumentException(
                    "tag() requires exactly one string literal argument, "
                        + "e.g. tag(\"KEY\")");
            }
            if (genCtx.inputType != null
                    && !LALCodegenHelper.LOGDATA_BUILDER_CLASS
                        .isAssignableFrom(genCtx.inputType)) {
                throw new IllegalArgumentException(
                    "tag() reads LogData tags but the input type is "
                        + genCtx.inputType.getName()
                        + ". Use a json{}/yaml{}/text{} parser, or access "
                        + "typed fields via parsed.* instead.");
            }
            sb.append("h.tagValue(\"");
            final String key = value.getFunctionCallArgs().get(0)
                .getValue().getSegments().get(0);
            sb.append(DslJavaSourceText.toLiteral(key)).append("\")");
            return;
        }

View on GitHub (pinned to 102af09b4a)

Solutions

  1. Align the data type: pass the type the function declares in AcceptableValue<T> (the error prints both type names)
  2. Or switch to a function whose generic parameter matches your data type
  3. Recompile custom functions and callers together so the generic contract stays consistent

Example fix

// before — function declares AcceptableValue<Long>
meterSystem.create("my_metric", "sum", ScopeType.SERVICE);
// custom create path passing Double
class LongSumFunction implements AcceptableValue<Long> { ... }
// after — use a function declaring AcceptableValue<Double>
class DoubleSumFunction implements AcceptableValue<Double> { ... }
Defensive patterns

Strategy: type-guard

Validate before calling

for (Type t : functionClass.getGenericInterfaces()) {
    if (t instanceof ParameterizedType && ((ParameterizedType) t).getRawType().getTypeName().endsWith("AcceptableValue")) {
        Type arg = ((ParameterizedType) t).getActualTypeArguments()[0];
        if (!arg.equals(dataType)) throw new IllegalArgumentException("type mismatch: " + arg);
    }
}

Type guard

static boolean acceptsType(Class<? extends AcceptableValue<?>> fn, Class<?> dataType) {
    for (Type t : fn.getGenericInterfaces()) {
        if (t instanceof ParameterizedType) {
            ParameterizedType p = (ParameterizedType) t;
            if (p.getRawType().getTypeName().endsWith("AcceptableValue")) {
                return p.getActualTypeArguments()[0].equals(dataType);
            }
        }
    }
    return false;
}

Prevention

When it happens

Trigger: A custom MeterSystem.create caller passing dataType=Double.class for a function declared AcceptableValue<Long>, or a MAL expression producing a value type inconsistent with the chosen function (e.g. a percent function expecting Long fed by a decimal-producing expression).

Common situations: Writing custom meter functions or direct MeterSystem integrations; changing a function's generic parameter without rebuilding dependent rules; MAL function selection that doesn't match the expression's numeric type.

Related errors


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