{"record":{"id":"79a2956b3e86f179","repo":"apache/skywalking","slug":"mal-compile-failed-for-sourcename","errorCode":null,"errorMessage":"MAL compile failed for {sourceName}","messagePattern":"MAL compile failed for (.+?)","errorType":"http","errorClass":"ApplyException","httpStatus":500,"severity":"error","filePath":"oap-server/server-admin/runtime-rule/src/main/java/org/apache/skywalking/oap/server/receiver/runtimerule/apply/MalFileApplier.java","lineNumber":172,"sourceCode":"            convert = new MetricConvert(rule, meterSystem, pool, ruleLoader, storageOpt);\n        } catch (final MetricConvert.PartialRegistrationException pre) {\n            // Phase-2 register threw partway. Carry ONLY the subset that actually landed in\n            // MeterSystem — the caller uses this set for rollback. Passing the full enumerated\n            // set here would remove metrics the old bundle still owns (disastrous on\n            // FILTER_ONLY edits, where by definition every metric name is also in the old\n            // bundle). The layer-registry changes are reverted now so a failed MeterSystem\n            // register does not leak a half-applied layer state.\n            layerRegistry.rollback(appliedClaims);\n            throw new ApplyException(\n                \"MAL register failed for \" + sourceName + \" (partial)\",\n                pre.getCause() == null ? pre : pre.getCause(),\n                pre.getRegisteredBeforeFailure());\n        } catch (final Throwable t) {\n            // Phase-1 compile failure or other pre-register throw. Nothing was registered with\n            // MeterSystem, so rollback set is empty — passing a non-empty set would cause the\n            // caller to unregister metrics the old bundle owns and this apply never touched.\n            layerRegistry.rollback(appliedClaims);\n            throw new ApplyException(\"MAL compile failed for \" + sourceName, t, Collections.emptySet());\n        }\n        // All DDL for this file's metrics is now fired. If the opt deferred its schema fence\n        // (batched apply via withSchemaChangeDeferredFence), run the single barrier here so the\n        // whole file waits ONCE instead of one fence per metric/downsampling. A fence timeout is\n        // a non-fatal WARN inside the closure; only a barrier transport error throws, which\n        // aborts this apply exactly as an inline per-resource fence would have.\n        //\n        // EXCEPTION: when fenceRunByCaller is set (the runtime-rule REST apply), the orchestrator\n        // runs the fence itself AFTER the durable commit + peer resume, on a background thread, so\n        // a long (3-min) cluster-propagation wait neither blocks the apply nor holds peers\n        // suspended. We only fire the DDL here and leave the closure for the caller to run.\n        if (!storageOpt.isFenceRunByCaller()) {\n            try {\n                storageOpt.runDeferredFence();\n            } catch (final StorageException e) {\n                layerRegistry.rollback(appliedClaims);\n                throw new ApplyException(\"schema fence failed for \" + sourceName, e, metricNames);\n            }","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/apache/skywalking/blob/102af09b4a56064e22050dded10e2c52e490d040/oap-server/server-admin/runtime-rule/src/main/java/org/apache/skywalking/oap/server/receiver/runtimerule/apply/MalFileApplier.java#L154-L190","documentation":"ApplyException thrown by MalFileApplier.apply for any phase-1 failure — content parsing/compilation or any pre-register throw that is not PartialRegistrationException. Because the failure happened before MeterSystem registration, the rollback set is deliberately EMPTY: passing a non-empty set would make the caller unregister metrics the still-installed old bundle owns. Layer-registry claims are rolled back first so a failed compile leaks no layer state.","triggerScenarios":"Applying MAL content that fails YAML parse or expression compile: bad exp syntax, missing metricPrefix, unknown MAL function, or metric rule names that fail validation — anything thrown by new MetricConvert(...) before any metric lands in MeterSystem.","commonSituations":"Invalid expression grammar after editing exp fields; Version skew: MAL functions added in a newer OAP used against an older server; Files missing required top-level keys (metricPrefix) so compile aborts immediately","solutions":["Inspect the cause for the exact compile/parse failure and fix the expression or schema","Pre-validate with the same parse path: Yaml().loadAs(reader, Rule.class) must yield a rule with metricPrefix and metricsRules","Align the MAL grammar version you author against the running OAP version (check the MAL docs for supported functions)","Re-apply once fixed; no cleanup is needed because nothing was registered (empty rollback set)"],"exampleFix":"# before\nexp: increase(total{inventory.recode_type='pod'},5m).scale(0.016666  # typo/truncation\n# after\nexp: \"increase(total{inventory.recode_type='pod'},5m).scale(0.016666)\"","handlingStrategy":"try-catch","validationCode":"try (StringReader r = new StringReader(content)) {\n    Rule rule = new Yaml().loadAs(r, Rule.class);\n    if (rule == null || rule.getMetricPrefix() == null || rule.getMetricsRules() == null) reject(\"incomplete MAL doc\");\n}","typeGuard":null,"tryCatchPattern":"catch (ApplyException e) when 'MAL compile failed': rollback set is empty by contract — do NOT unregister anything; fix the compile error from e.getCause() and re-apply fresh.","preventionTips":["Pre-compile MAL expressions in CI against the same OAP version","Validate metricPrefix presence before submit","Remember phase-1 failures register nothing — cleanup code must not touch the old bundle"],"tags":["mal","compile","runtime-rule","rollback"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}