apache/skywalking · error · IllegalExpressionException

Unsupported operation.

Error message

Unsupported operation.

What it means

The core IllegalExpressionException from LROp.doLROp's type dispatch: the left/right ExpressionResultType combination matches none of the supported branches — SINGLE_VALUE×SINGLE_VALUE (scalar path, handled earlier), many(TIME_SERIES/SORTED_LIST/RECORD_LIST)×SINGLE, SINGLE×many, or TIME_SERIES×TIME_SERIES. This is the root cause message that BinaryOp, CompareOp and BoolOp re-wrap in their 'Unsupported X operation' errors.

Source

Thrown at oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/operation/LROp.java:57

                                          ExpressionResult right,
                                          int opType, LROp calculate) throws IllegalExpressionException {
        if (left.getType() == ExpressionResultType.SINGLE_VALUE && right.getType() == ExpressionResultType.SINGLE_VALUE) {
            return single2SingleBinaryOp(left, right, opType, calculate);
        } else if ((left.getType() == ExpressionResultType.TIME_SERIES_VALUES ||
            left.getType() == ExpressionResultType.SORTED_LIST ||
            left.getType() == ExpressionResultType.RECORD_LIST)
            && right.getType() == ExpressionResultType.SINGLE_VALUE) {
            return many2OneBinaryOp(left, right, opType, calculate);
        } else if (left.getType() == ExpressionResultType.SINGLE_VALUE &&
            (right.getType() == ExpressionResultType.TIME_SERIES_VALUES ||
                right.getType() == ExpressionResultType.SORTED_LIST ||
                right.getType() == ExpressionResultType.RECORD_LIST)) {
            return one2ManyBinaryOp(left, right, opType, calculate);
        } else if (left.getType() == ExpressionResultType.TIME_SERIES_VALUES && right.getType() == ExpressionResultType.TIME_SERIES_VALUES) {
            return seriesBinaryOp(left, right, opType, calculate);
        }

        throw new IllegalExpressionException("Unsupported operation.");
    }

    //series with series
    private static ExpressionResult seriesBinaryOp(ExpressionResult seriesLeft,
                                                   ExpressionResult seriesRight,
                                                   int opType, LROp calculate) throws IllegalExpressionException {
        ExpressionResult result = new ExpressionResult();
        if (!seriesLeft.isLabeledResult() && !seriesRight.isLabeledResult()) { // no labeled with no labeled
            result = seriesNoLabeled(seriesLeft, seriesRight, opType, calculate);
        } else if (seriesLeft.isLabeledResult() && !seriesRight.isLabeledResult()) { // labeled with no labeled
            result = seriesLabeledWithNoLabeled(seriesLeft, seriesRight, opType, calculate);
        } else if (!seriesLeft.isLabeledResult() && seriesRight.isLabeledResult()) { // no labeled with labeled
            result = seriesNoLabeledWithLabeled(seriesLeft, seriesRight, opType, calculate);
        } else { // labeled with labeled
            result = seriesLabeledWithLabeled(seriesLeft, seriesRight, opType, calculate);
        }

        return result;

View on GitHub (pinned to 102af09b4a)

Solutions

  1. Identify both operand types (UI shows the result type of each sub-expression, or consult the MQE docs) and reshape so the pair is series×series, or one side a single value
  2. Apply aggregations to reduce lists to series before combining: aggregate_labels(x, SUM), or restructure so topn/records sit outside the arithmetic
  3. Compare/boolean operators additionally require boolean leaves — see the BoolOp-specific errors
  4. Simplify the expression into stages and verify each stage renders alone in the UI before combining

Example fix

# before
topn(service_cpm.sum(dt5m), 10, DES) + topn(service_sla.sum(dt5m), 10, DES)
# after
(service_cpm.sum(dt5m) + service_sla.sum(dt5m))  # arithmetic on series; apply topn afterwards if needed
Defensive patterns

Strategy: type-guard

Validate before calling

boolean isLROpSupported(ExpressionResultType l, ExpressionResultType r) {
    boolean manyL = l == TIME_SERIES_VALUES || l == SORTED_LIST || l == RECORD_LIST;
    boolean manyR = r == TIME_SERIES_VALUES || r == SORTED_LIST || r == RECORD_LIST;
    return (l == SINGLE_VALUE && r == SINGLE_VALUE)
        || (manyL && r == SINGLE_VALUE) || (l == SINGLE_VALUE && manyR)
        || (l == TIME_SERIES_VALUES && r == TIME_SERIES_VALUES);
}

Type guard

boolean isBinaryOperandType(org.apache.skywalking.oap.server.core.query.mqe.ExpressionResultType t) {
    return t == org.apache.skywalking.oap.server.core.query.mqe.ExpressionResultType.SINGLE_VALUE
        || t == org.apache.skywalking.oap.server.core.query.mqe.ExpressionResultType.TIME_SERIES_VALUES;
}

Try / catch

catch (IllegalExpressionException e) { /* reshape: aggregate lists to series, or move topn/records outside the binary op */ }

Prevention

When it happens

Trigger: Any MQE binary operation between two 'many' operands where at least one is not a time series: LIST×LIST, RECORD_LIST×SERIES, SORTED_LIST×RECORD_LIST, SINGLE×SINGLE arriving on the non-scalar path, etc. Example: 'topn(a,10) + topn(b,10)', 'queryLogs(...) > metric'.

Common situations: Combining two TopN results; mixing record queries (traces/logs) with metric arithmetic; combining labeled series under unsupported label-pairing branches; building complex dashboard expressions without checking each intermediate's result type.

Related errors


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