apache/skywalking · error · IllegalExpressionException

Expression: {expression} is not a SINGLE_VALUE result expres

Error message

Expression: {expression} is not a SINGLE_VALUE result expression.

What it means

Thrown while parsing an alarm rule whose MQE (Metrics Query Expression) expression does not evaluate to a SINGLE_VALUE result. Alarm rules evaluate one number per entity per period, so expressions that return a time series (e.g. anything using the gen() family producing multiple points) are rejected. The check happens at AlarmRule construction time, i.e. when alarm-settings.yml is loaded at OAP startup or on dynamic config refresh.

Source

Thrown at oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRule.java:98

        ParseTree tree;
        try {
            tree = parser.expression();
        } catch (ParseCancellationException e) {
            throw new IllegalExpressionException("Expression: " + expression + " error: " + e.getMessage());
        }
        try {
            TRACE_CONTEXT.set(new DebuggingTraceContext(expression, false, false));
            AlarmMQEVerifyVisitor visitor = new AlarmMQEVerifyVisitor(moduleManager);
            ExpressionResult parseResult = visitor.visit(tree);
            if (StringUtil.isNotBlank(parseResult.getError())) {
                throw new IllegalExpressionException("Expression: " + expression + " error: " + parseResult.getError());
            }
            if (!parseResult.isBoolResult()) {
                throw new IllegalExpressionException(
                    "Expression: " + expression + " root operation is not a Compare Operation.");
            }
            if (ExpressionResultType.SINGLE_VALUE != parseResult.getType()) {
                throw new IllegalExpressionException(
                    "Expression: " + expression + " is not a SINGLE_VALUE result expression.");
            }

            verifyIncludeMetrics(visitor.getIncludeMetrics(), expression);
            this.expression = expression;
            this.includeMetrics = visitor.getIncludeMetrics();
            this.maxTrendRange = visitor.getMaxTrendRange();
        } finally {
            TRACE_CONTEXT.remove();
        }
    }

    private void verifyIncludeMetrics(Set<String> includeMetrics, String expression) throws IllegalExpressionException {
        Set<String> scopeSet = new HashSet<>();
        for (String metricName : includeMetrics) {
            scopeSet.add(ValueColumnMetadata.INSTANCE.getScope(metricName).name());
        }
        if (scopeSet.size() != 1) {

View on GitHub (pinned to 102af09b4a)

Solutions

  1. Wrap the series expression with an aggregator that yields one value, e.g. `latest(service_resp_time) > 1000` or `avg(...)` instead of a raw/gen expression.
  2. Verify the expression in the UI's Metrics Query (MQE) panel first and confirm the result column is a single value per entity, not a trend.
  3. Check the MQE grammar docs (docs/en/api/metrics-query-expression.md) for which functions return TIME_SERIES vs SINGLE_VALUE and combine them accordingly.
  4. Re-run OAP or trigger the dynamic config update only after the expression validates; the exception aborts the whole alarm settings load.

Example fix

# before (alarm-settings.yml)
rules:
  endpoint_resp_rule:
    expression: gen(service_resp_time,5) > 1000  # returns TIME_SERIES
# after
rules:
  endpoint_resp_rule:
    expression: latest(service_resp_time) > 1000  # SINGLE_VALUE
Defensive patterns

Strategy: validation

Validate before calling

// Before pushing the rule, run the expression through the MQE parser (same as AlarmRule does):
// org.apache.skywalking.oap.server.mqe.rt.MockMQEEndpoint # or in a test:
//   expression must yield ExpressionResultType.SINGLE_VALUE and isBoolResult()==true
// Quick manual check: query the same expression via the UI Metrics/Trace MQE panel;
// the result must be one value per entity (no time column), and the top op must be a comparison.

Type guard

// Java (unit test guard for alarm expressions)
boolean isSingleValueBoolExpression(ExpressionResult r) {
    return r != null && StringUtil.isBlank(r.getError())
        && r.isBoolResult()
        && r.getType() == ExpressionResultType.SINGLE_VALUE;
}

Try / catch

// When loading alarm settings programmatically
catch (IllegalExpressionException e) {
    log.error("Alarm rule {} rejected: {}", ruleName, e.getMessage()); // keep old rule set, skip this rule
}

Prevention

When it happens

Trigger: An alarm rule expression like `sum(service_resp_time > 1000)` style queries that resolve to ExpressionResultType.TIME_SERIES instead of SINGLE_VALUE; typically caused by using a trend/multiple-value function (gen...) in the alarm `expression` field, or omitting an aggregation such as latest() that collapses a series to a single value.

Common situations: Copying an expression from the UI's MQE query tab (which happily renders time series) into alarm-settings.yml; upgrading OAP versions where alarm expressions moved from the old OAL-like syntax to MQE and old expressions no longer collapse to one value.

Related errors


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