{"record":{"id":"fb5dab0e4833d8a0","repo":"apache/skywalking","slug":"expression-expression-is-not-a-single-value-res","errorCode":null,"errorMessage":"Expression: {expression} is not a SINGLE_VALUE result expression.","messagePattern":"Expression: (.+?) is not a SINGLE_VALUE result expression\\.","errorType":"validation","errorClass":"IllegalExpressionException","httpStatus":null,"severity":"error","filePath":"oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRule.java","lineNumber":98,"sourceCode":"        ParseTree tree;\n        try {\n            tree = parser.expression();\n        } catch (ParseCancellationException e) {\n            throw new IllegalExpressionException(\"Expression: \" + expression + \" error: \" + e.getMessage());\n        }\n        try {\n            TRACE_CONTEXT.set(new DebuggingTraceContext(expression, false, false));\n            AlarmMQEVerifyVisitor visitor = new AlarmMQEVerifyVisitor(moduleManager);\n            ExpressionResult parseResult = visitor.visit(tree);\n            if (StringUtil.isNotBlank(parseResult.getError())) {\n                throw new IllegalExpressionException(\"Expression: \" + expression + \" error: \" + parseResult.getError());\n            }\n            if (!parseResult.isBoolResult()) {\n                throw new IllegalExpressionException(\n                    \"Expression: \" + expression + \" root operation is not a Compare Operation.\");\n            }\n            if (ExpressionResultType.SINGLE_VALUE != parseResult.getType()) {\n                throw new IllegalExpressionException(\n                    \"Expression: \" + expression + \" is not a SINGLE_VALUE result expression.\");\n            }\n\n            verifyIncludeMetrics(visitor.getIncludeMetrics(), expression);\n            this.expression = expression;\n            this.includeMetrics = visitor.getIncludeMetrics();\n            this.maxTrendRange = visitor.getMaxTrendRange();\n        } finally {\n            TRACE_CONTEXT.remove();\n        }\n    }\n\n    private void verifyIncludeMetrics(Set<String> includeMetrics, String expression) throws IllegalExpressionException {\n        Set<String> scopeSet = new HashSet<>();\n        for (String metricName : includeMetrics) {\n            scopeSet.add(ValueColumnMetadata.INSTANCE.getScope(metricName).name());\n        }\n        if (scopeSet.size() != 1) {","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/apache/skywalking/blob/102af09b4a56064e22050dded10e2c52e490d040/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRule.java#L80-L116","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before (alarm-settings.yml)\nrules:\n  endpoint_resp_rule:\n    expression: gen(service_resp_time,5) > 1000  # returns TIME_SERIES\n# after\nrules:\n  endpoint_resp_rule:\n    expression: latest(service_resp_time) > 1000  # SINGLE_VALUE","handlingStrategy":"validation","validationCode":"// Before pushing the rule, run the expression through the MQE parser (same as AlarmRule does):\n// org.apache.skywalking.oap.server.mqe.rt.MockMQEEndpoint # or in a test:\n//   expression must yield ExpressionResultType.SINGLE_VALUE and isBoolResult()==true\n// Quick manual check: query the same expression via the UI Metrics/Trace MQE panel;\n// the result must be one value per entity (no time column), and the top op must be a comparison.","typeGuard":"// Java (unit test guard for alarm expressions)\nboolean isSingleValueBoolExpression(ExpressionResult r) {\n    return r != null && StringUtil.isBlank(r.getError())\n        && r.isBoolResult()\n        && r.getType() == ExpressionResultType.SINGLE_VALUE;\n}","tryCatchPattern":"// When loading alarm settings programmatically\ncatch (IllegalExpressionException e) {\n    log.error(\"Alarm rule {} rejected: {}\", ruleName, e.getMessage()); // keep old rule set, skip this rule\n}","preventionTips":["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."],"tags":["alarm","mqe","config","startup"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}