{"record":{"id":"4ba686218cf8a268","repo":"larksuite/cli","slug":"parse-policy-yaml-top-level-rule-fields-cannot-be","errorCode":null,"errorMessage":"parse policy yaml: top-level rule fields cannot be combined with a 'rules:' list; move every rule under 'rules:'","messagePattern":"parse policy yaml: top-level rule fields cannot be combined with a 'rules:' list; move every rule under 'rules:'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmdpolicy/yaml/schema.go","lineNumber":124,"sourceCode":"\t}\n\n\t// Reject multi-document input: yaml.v3 only decodes one document\n\t// per call, so a stray \"---\" followed by another document would\n\t// silently drop the trailing rule.\n\tvar extra fileSchema\n\tif err := dec.Decode(&extra); !errors.Is(err, io.EOF) {\n\t\tif err == nil {\n\t\t\treturn nil, fmt.Errorf(\"parse policy yaml: multiple YAML documents are not allowed\")\n\t\t}\n\t\treturn nil, fmt.Errorf(\"parse policy yaml: %w\", err)\n\t}\n\n\tif s.Rules != nil {\n\t\tif len(*s.Rules) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"parse policy yaml: 'rules:' is present but empty; remove the key, or list at least one rule\")\n\t\t}\n\t\tif !s.ruleSchema.isZero() {\n\t\t\treturn nil, fmt.Errorf(\"parse policy yaml: top-level rule fields cannot be combined with a 'rules:' list; move every rule under 'rules:'\")\n\t\t}\n\t\tout := make([]*platform.Rule, 0, len(*s.Rules))\n\t\tfor _, rs := range *s.Rules {\n\t\t\tout = append(out, rs.toRule())\n\t\t}\n\t\treturn out, nil\n\t}\n\n\t// Backward-compatible single top-level rule (flat fields).\n\treturn []*platform.Rule{s.ruleSchema.toRule()}, nil\n}\n","sourceCodeStart":106,"sourceCodeEnd":136,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/cmdpolicy/yaml/schema.go#L106-L136","documentation":"fileSchema accepts two mutually exclusive layouts: flat top-level rule fields (historical single-rule layout, inlined ruleSchema) or a 'rules:' list. Mixing both is ambiguous, so when 'rules:' is present and any flat field (name, allow, deny, max_risk, identities, allow_unannotated) is also set, Parse rejects the document instead of guessing intent.","triggerScenarios":"Calling Parse on YAML that has both a 'rules:' list and at least one top-level rule field, e.g. 'max_risk: read' at the top plus a 'rules:' list, or a leftover top-level 'name:' alongside rules.","commonSituations":"Migrating a flat single-rule file to the multi-rule layout without moving old flat fields under 'rules:'; merge-conflict resolutions keeping both styles; appending a rules list to an existing flat policy file.","solutions":["Move every flat rule field into entries under 'rules:'.","Or delete the 'rules:' key if the single flat rule is intended.","Remove stale top-level keys left over from migration."],"exampleFix":"// before\nmax_risk: read\nrules:\n  - name: a\n// after\nrules:\n  - name: a\n    max_risk: read","handlingStrategy":"validation","validationCode":"var probe struct {\n\tRules []any    `yaml:\"rules\"`\n\tName  string   `yaml:\"name\"`\n\tAllow []string `yaml:\"allow\"`\n}\nif err := yaml.Unmarshal(data, &probe); err == nil && probe.Rules != nil && (probe.Name != \"\" || len(probe.Allow) > 0) {\n\treturn fmt.Errorf(\"mixed flat fields and rules: list\")\n}","typeGuard":null,"tryCatchPattern":"if _, err := yaml.Parse(data); err != nil && strings.Contains(err.Error(), \"cannot be combined\") {\n\treturn fmt.Errorf(\"migrate fully to the rules: layout: %w\", err)\n}","preventionTips":["Pick one layout per file; prefer the 'rules:' list for new policies.","When migrating a flat file, move every field under 'rules:' in the same edit.","Watch for merge conflicts that re-introduce flat fields above a rules list.","Lint policy files to fail on top-level rule keys when 'rules:' is present."],"tags":["go","yaml","config","schema-conflict"],"backgroundTag":"config-schema-conflict","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}