{"record":{"id":"6568d9b889055fac","repo":"apache/skywalking","slug":"lal-yaml-parsed-to-empty-malformed-no-rules-list","errorCode":null,"errorMessage":"LAL YAML parsed to empty/malformed — no rules list in {sourceName}","messagePattern":"LAL YAML parsed to empty/malformed — no rules list in (.+?)","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/LalFileApplier.java","lineNumber":326,"sourceCode":"                            + \"skipping from teardown enumeration\", c.getName(), c.getLayer(), sourceName);\n                        continue;\n                    }\n                }\n                out.add(new RegisteredRule(layer, c.getName()));\n            }\n            return Collections.unmodifiableList(out);\n        } catch (final Throwable t) {\n            log.warn(\"runtime-rule: failed to parse static LAL content for {} — no rule keys \"\n                + \"enumerated for teardown\", sourceName, t);\n            return Collections.emptyList();\n        }\n    }\n\n    private LALConfigs parse(final String yamlContent, final String sourceName) throws ApplyException {\n        try (StringReader reader = new StringReader(yamlContent)) {\n            final LALConfigs configs = new Yaml().loadAs(reader, LALConfigs.class);\n            if (configs == null || configs.getRules() == null || configs.getRules().isEmpty()) {\n                throw new ApplyException(\n                    \"LAL YAML parsed to empty/malformed — no rules list in \" + sourceName,\n                    null, Collections.emptyList());\n            }\n            // Resolve each rule's line from the SAME text, exactly as the boot loader does.\n            // Without this a hot-updated rule compiles to an unlabelled class while its\n            // disk-loaded twin is labelled — the two routes must agree.\n            final DslYamlLineIndex lineIndex = DslYamlLineIndex.index(yamlContent, \"rules\");\n            for (int i = 0; i < configs.getRules().size(); i++) {\n                configs.getRules().get(i).setLineNo(lineIndex.rule(i).getEntryLine());\n            }\n            // layerDefinitions: are now permitted in runtime LAL rules; the apply path\n            // funnels them through the runtime-layer registry. The rejection that used to\n            // live here was removed when runtime dynamic layers became a first-class feature.\n            return configs;\n        } catch (final ApplyException e) {\n            throw e;\n        } catch (final Throwable t) {\n            throw new ApplyException(\"LAL YAML parse failure for \" + sourceName, t,","sourceCodeStart":308,"sourceCodeEnd":344,"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/LalFileApplier.java#L308-L344","documentation":"ApplyException thrown by LalFileApplier.parse when snakeyaml loads the content but the result is null, has no 'rules' key, or rules is empty — i.e. the YAML is syntactically loadable but not a valid LAL document. Cause is null and the partial list is empty because nothing was applied. Note this fires before per-rule line indexing (DslYamlLineIndex), so it also guards against documents whose top-level structure is a list or a string instead of the expected mapping with 'rules:'","triggerScenarios":"Submitting runtime LAL content such as 'rules:' with nothing under it, a YAML doc whose top key is 'defaultFG'/'layerDefinitions' only, an empty file, or a YAML mapping that deserializes into LALConfigs with a null rules field (e.g. rules spelled 'Rules').","commonSituations":"Deploying a file with only layerDefinitions and forgetting the rules block; Env-substitution pipeline producing an effectively empty body; Rules list commented out during debugging and shipped anyway; Key casing drift ('Rules:' vs 'rules:') from schema changes in tooling","solutions":["Ensure the document has a non-empty top-level 'rules:' list with at least one rule entry","Verify with a quick local check: loadAs must yield configs != null && configs.getRules() != null && !isEmpty","If you intended a layer-only update, use the layer-definition API path instead of an LAL file apply","Check that template rendering didn't strip the rules section (empty variables)"],"exampleFix":"# before\nlayerDefinitions:\n  - name: my-layer\n# after (an LAL file must carry rules)\nlayerDefinitions:\n  - name: my-layer\nrules:\n  - name: first-rule\n    layer: my-layer\n    exp: \"true\"","handlingStrategy":"validation","validationCode":"try (StringReader r = new StringReader(content)) {\n    LALConfigs c = new Yaml().loadAs(r, LALConfigs.class);\n    if (c == null || c.getRules() == null || c.getRules().isEmpty()) reject(\"empty rules list\");\n}","typeGuard":null,"tryCatchPattern":"catch (ApplyException e) when 'parsed to empty/malformed': reject at the upload boundary — this is pure input validation, never retry the same content.","preventionTips":["Reject empty or rules-less LAL files at the API client before upload","Make CI fail on template artifacts whose rendered rules block is empty","Use the dedicated layer API for layer-only changes instead of LAL files"],"tags":["yaml","lal","validation","runtime-rule"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}