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
- 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.
- 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.
- 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.
- 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
- Always prototype alarm expressions in the UI MQE panel before writing them into alarm-settings.yml.
- Wrap trend functions with latest()/avg()/sum() so the result collapses to a single value.
- Keep a unit test that constructs each AlarmRule from the shipped alarm-settings.yml so bad expressions fail in CI, not in prod startup.
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
- The metrics in expression: {expression} must have the same s
- expression can't be empty
- Failed to load LAL config rules
- LAL rule {ruleName} layerDefinitions entry rejected: {def}
- Failed to load MAL rules
AI-assisted analysis of apache/skywalking@102af09b4a (2026-08-14).
Data as JSON: /api/errors/fb5dab0e4833d8a0.
Report an issue: GitHub.