{"record":{"id":"e53d0e1665dc7d2b","repo":"apache/skywalking","slug":"expression-can-t-be-empty","errorCode":null,"errorMessage":"expression can't be empty","messagePattern":"expression can't be empty","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReader.java","lineNumber":105,"sourceCode":"    }\n\n    /**\n     * Read rule config into {@link AlarmRule}\n     */\n    private void readRulesConfig(Rules rules) {\n        Map rulesData = (Map) yamlData.get(\"rules\");\n        if (rulesData == null) {\n            return;\n        }\n        rules.setRules(new ArrayList<>());\n        rulesData.forEach((k, v) -> {\n            if (((String) k).endsWith(\"_rule\")) {\n                AlarmRule alarmRule = new AlarmRule(moduleManager);\n                alarmRule.setAlarmRuleName((String) k);\n                Map settings = (Map) v;\n                Object expression = settings.get(\"expression\");\n                if (StringUtil.isEmpty((String) expression)) {\n                    throw new IllegalArgumentException(\"expression can't be empty\");\n                }\n                try {\n                    alarmRule.setExpression(expression.toString());\n                } catch (IllegalExpressionException e) {\n                    throw new IllegalArgumentException(e);\n                }\n                alarmRule.setIncludeNames((ArrayList) settings.getOrDefault(\"include-names\", new ArrayList(0)));\n                alarmRule.setExcludeNames((ArrayList) settings.getOrDefault(\"exclude-names\", new ArrayList(0)));\n                alarmRule.setIncludeNamesRegex((String) settings.getOrDefault(\"include-names-regex\", \"\"));\n                alarmRule.setExcludeNamesRegex((String) settings.getOrDefault(\"exclude-names-regex\", \"\"));\n                alarmRule.setPeriod((Integer) settings.getOrDefault(\"period\", 1));\n                // How many times of checks, the alarm keeps silence after alarm triggered, default as same as period.\n                alarmRule.setSilencePeriod((Integer) settings.getOrDefault(\"silence-period\", alarmRule.getPeriod()));\n                alarmRule.setRecoveryObservationPeriod((Integer) settings.getOrDefault(\"recovery-observation-period\",\n                        0));\n                alarmRule.setMessage(\n                        (String) settings.getOrDefault(\"message\", \"Alarm caused by Rule \" + alarmRule\n                                .getAlarmRuleName()));","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/apache/skywalking/blob/102af09b4a56064e22050dded10e2c52e490d040/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReader.java#L87-L123","documentation":"IllegalArgumentException thrown by RulesReader while loading alarm-settings.yml when a `*_rule` entry has an empty or missing `expression` key. Every alarm rule must carry an expression; the reader fails fast during YAML parsing so the OAP does not start (or the dynamic config update fails) with a half-initialized rule set.","triggerScenarios":"A rules map entry whose key ends with `_rule` but whose settings map has no `expression` key, or expression: \"\" (empty string / null after YAML parsing). RulesReader calls alarmRule.setExpression(expression.toString()) only after StringUtil.isEmpty passes, so any blank value hits this branch.","commonSituations":"Creating a new rule by copy-paste and deleting the expression line; YAML indentation that accidentally places `expression` under a sibling key; setting expression: null while templating alarm-settings via Helm/Kustomize; a commented-out line (`#expression: ...`) that removes the only expression.","solutions":["Add a valid MQE expression to the rule entry, e.g. `expression: sum(service_resp_time > 1000) >= 3`.","Check YAML indentation — `expression` must be a direct child of the rule name, same level as `period`, `threshold`, etc.","If the rule is intentionally disabled, remove or rename the key so it does not end with `_rule` instead of leaving it expression-less.","Validate alarm-settings.yml with a YAML linter before deploying; the exception aborts the entire rules load, not just this rule."],"exampleFix":"# before\nrules:\n  service_sla_rule:\n    threshold: 1\n# after\nrules:\n  service_sla_rule:\n    expression: sum(service_sla < 90) >= 3\n    threshold: 1","handlingStrategy":"validation","validationCode":"# YAML pre-check before deploying alarm-settings.yml\npython3 -c \"\nimport yaml,sys\nrules = yaml.safe_load(open('alarm-settings.yml')).get('rules') or {}\nfor name, s in rules.items():\n    if name.endswith('_rule') and not (s.get('expression') or '').strip():\n        sys.exit(f'{name}: expression missing/empty')\n\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Template alarm-settings.yml with required-field linting (yamllint plus a schema check).","Treat expression as a mandatory key in any internal rule authoring UI or CRD schema.","Never comment out expression to 'disable' a rule — delete or rename the rule key instead."],"tags":["alarm","yaml","config","startup"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}