{"record":{"id":"e3c756af58e162f2","repo":"apache/skywalking","slug":"mal-expression-parsing-failed-while-injecting-expp","errorCode":null,"errorMessage":"MAL expression parsing failed while injecting expPrefix: {} in expression: {}","messagePattern":"MAL expression parsing failed while injecting expPrefix: (.+?) in expression: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/v2/compiler/MALScriptParser.java","lineNumber":135,"sourceCode":"        final MALLexer lexer = new MALLexer(CharStreams.fromString(exp));\n        final CommonTokenStream tokens = new CommonTokenStream(lexer);\n        final MALParser parser = new MALParser(tokens);\n        final List<String> errors = new ArrayList<>();\n        parser.removeErrorListeners();\n        parser.addErrorListener(new BaseErrorListener() {\n            @Override\n            public void syntaxError(final Recognizer<?, ?> recognizer,\n                                    final Object offendingSymbol,\n                                    final int line,\n                                    final int charPositionInLine,\n                                    final String msg,\n                                    final RecognitionException e) {\n                errors.add(line + \":\" + charPositionInLine + \" \" + msg);\n            }\n        });\n        final MALParser.ExpressionContext tree = parser.expression();\n        if (!errors.isEmpty()) {\n            throw new IllegalArgumentException(\n                \"MAL expression parsing failed while injecting expPrefix: \"\n                    + String.join(\"; \", errors) + \" in expression: \" + exp);\n        }\n        final List<int[]> ranges = new ArrayList<>();\n        collectMetricSourceRanges(tree, ranges);\n        // Splice from right to left so earlier indices remain valid.\n        ranges.sort(Comparator.comparingInt((int[] r) -> r[0]).reversed());\n        final StringBuilder sb = new StringBuilder(exp);\n        for (final int[] range : ranges) {\n            final String name = sb.substring(range[0], range[1] + 1);\n            sb.replace(range[0], range[1] + 1, \"(\" + name + \".\" + expPrefix + \")\");\n        }\n        return sb.toString();\n    }\n\n    private static void collectMetricSourceRanges(final ParseTree node,\n                                                  final List<int[]> ranges) {\n        if (node instanceof MALParser.PrimaryContext) {","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/apache/skywalking/blob/102af09b4a56064e22050dded10e2c52e490d040/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/v2/compiler/MALScriptParser.java#L117-L153","documentation":"Thrown by MALScriptParser.injectExpPrefix when the base expression in a rule's exp: field fails to parse with the MAL ANTLR grammar while the compiler is splicing the file-level expPrefix (e.g. tag({tags -> ...})) into every metric source. The error listener accumulates line:column messages from ANTLR, and any syntax error aborts injection before text splicing happens.","triggerScenarios":"MetricConvert.formatExp calls injectExpPrefix(exp, expPrefix); it throws when the exp text has a grammar error — unbalanced parentheses, a stray character, an unsupported operator, or an identifier that does not lex as a metric source. Note this fires on the raw exp BEFORE prefix splicing, so the prefix itself is usually not the culprit.","commonSituations":"Hand-editing an otel-rules/meter-analyzer-config YAML and breaking the expression (missing ')', using '||' where MAL only supports specific boolean ops, smart quotes pasted from a doc); files that set a file-level expPrefix make every rule in the file go through this path, so one bad expr surfaces with this message.","solutions":["Read the line:column in the message — it locates the syntax error in the exp: string itself","Fix the expression grammar (balance parens/brackets, double-quote strings, use supported operators)","Sanity-parse the expression in isolation (unit test with MALScriptParser.parse or the MalRuleLoader.formatExp helper) to separate exp errors from expPrefix errors","Check for invisible characters from copy-paste (smart quotes, non-breaking spaces)"],"exampleFix":"# before\nexp: http_success_request.sum(['idc'].service(['idc'])\n\n# after\nexp: http_success_request.sum(['idc']).service(['idc'])","handlingStrategy":"try-catch","validationCode":"// fail early in tests before deploying\ntry {\n    String combined = MALScriptParser.injectExpPrefix(exp, expPrefix);\n} catch (IllegalArgumentException e) {\n    throw new AssertionError(\"Bad exp: \" + e.getMessage());\n}","typeGuard":null,"tryCatchPattern":"catch IllegalArgumentException around injectExpPrefix/formatExp in a rule-loading harness; report file + rule name, and fail the deployment rather than skipping the rule silently","preventionTips":["Run all custom rule YAMLs through a parse test (MalRuleLoader.formatExp + DSL.parse) in CI","Prefer per-rule expressions over clever file-level expPrefix blocks to keep failures isolated","Use a YAML-aware editor and lint unbalanced brackets"],"tags":["mal","parser","antlr","syntax","configuration"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}