apache/skywalking · error · IllegalArgumentException
Operator '{opSymbol}' requires numeric operands; got non-num
Error message
Operator '{opSymbol}' requires numeric operands; got non-numeric expression. Cast operands with 'as Integer/Long/Float/Double' to enable arithmetic. What it means
Thrown at LAL compile time when a binary arithmetic operator other than + is applied to an operand the type inference does not consider numeric. Only + has a string-concatenation fallback; -, *, /, % require both sides to be statically numeric. Since parsed values from json{}/yaml{} are untyped Objects, the codegen cannot prove numericness and rejects the expression, suggesting an explicit 'as' cast.
Source
Thrown at oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/v2/compiler/LALValueCodegen.java:1561
String accExpr = accBuf.toString();
for (int i = 1; i < parts.size(); i++) {
final LALScriptModel.BinaryOp op = ops.get(i - 1);
final ExprType rhsType = inferType(parts.get(i), genCtx);
final StringBuilder rhsBuf = new StringBuilder();
appendOperandRaw(rhsBuf, parts.get(i), rhsType, genCtx);
final String rhsExpr = rhsBuf.toString();
if (accType.isNumeric() && rhsType.isNumeric()) {
final ExprType promoted = promote(accType, rhsType);
final String widenedAcc = widenPrimitiveExpr(accExpr, accType, promoted);
final String widenedRhs = widenPrimitiveExpr(rhsExpr, rhsType, promoted);
accExpr = "(" + widenedAcc + " " + op.symbol() + " " + widenedRhs + ")";
accType = promoted;
continue;
}
if (op != LALScriptModel.BinaryOp.PLUS) {
throw new IllegalArgumentException(
"Operator '" + op.symbol() + "' requires numeric operands; "
+ "got non-numeric expression. Cast operands with "
+ "'as Integer/Long/Float/Double' to enable arithmetic.");
}
// String concat path. Java's `+` only accepts the chain when
// at least one operand is statically a String; otherwise (e.g.
// `int + Object` or `Object + Object`) the source won't even
// compile. Prepend a leading `""` whenever neither side is
// statically String at the transition point. The numeric
// arithmetic prefix is already in parens, so `("" + (1 + 2))`
// still computes the sum first ("3"), preserving the
// semantics of `1 + 2 + parsed.x` (= "3<obj>").
if (accType != ExprType.STRING && rhsType != ExprType.STRING) {
accExpr = "\"\" + " + accExpr;
}
accExpr = accExpr + " + " + rhsExpr;
accType = ExprType.STRING;
}View on GitHub (pinned to 102af09b4a)
Solutions
- Cast every operand with an explicit 'as' cast, e.g. (parsed.latency as Long) - (parsed.base as Long)
- Cast at least the accumulator-side of the chain so inference promotes correctly: parsed.latency as Long - 100
- For string concatenation use + (it falls back to concat), not other operators
Example fix
# before timestamp parsed.ts - parsed.offset # after timestamp (parsed.ts as Long) - (parsed.offset as Long)
Defensive patterns
Strategy: validation
Prevention
- Add 'as Long/Integer/Float/Double' casts to every parsed operand in arithmetic
- Reserve + for concatenation when any side may be a string
- Test metric arithmetic rules against sample log data in dsl-scripts-test
When it happens
Trigger: parsed.latency - 100 where parsed.* is map-typed (Object); parsed.count * parsed.factor without casts; arithmetic on safe-navigated chains (parsed?.x / 2) whose static type is Object.
Common situations: Computing latency/duration deltas from parsed JSON fields; porting Groovy v1 rules that did dynamic numeric coercion at runtime; metric expressions in the extractor that mix strings and numbers.
Related errors
- tag() reads LogData tags but the input type is {inputTypeNam
- sourceAttribute() requires exactly one string literal argume
- Cannot resolve {desc} — no matching codegen path. Parser typ
- Unknown log field: log.{firstName}. Supported metadata field
- Unknown log field: log.{name}. Supported metadata fields: {m
AI-assisted analysis of apache/skywalking@102af09b4a (2026-08-14).
Data as JSON: /api/errors/938ad7f5685e7dd0.
Report an issue: GitHub.