{"record":{"id":"0405a1aa5fb722a3","repo":"goharbor/harbor","slug":"empty-rule-template-id","errorCode":null,"errorMessage":"empty rule template ID","messagePattern":"empty rule template ID","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"src/pkg/retention/policy/rule/index/index.go","lineNumber":185,"sourceCode":"\n// Register the rule evaluator with the corresponding rule template\nfunc Register(meta *Metadata, factory rule.Factory, validator ...rule.Validator) {\n\tif meta == nil || factory == nil || len(meta.TemplateID) == 0 {\n\t\t// do nothing\n\t\treturn\n\t}\n\n\tindex.Store(meta.TemplateID, &indexedItem{\n\t\tMeta:       meta,\n\t\tFactory:    factory,\n\t\tValidators: validator,\n\t})\n}\n\n// Valid ...\nfunc Valid(templateID string, parameters rule.Parameters) error {\n\tif len(templateID) == 0 {\n\t\treturn errors.New(\"empty rule template ID\")\n\t}\n\n\tv, ok := index.Load(templateID)\n\tif !ok {\n\t\treturn errors.Errorf(\"rule evaluator %s is not registered\", templateID)\n\t}\n\n\titem := v.(*indexedItem)\n\n\t// We can check more things if we want to do in the future\n\tif len(item.Meta.Parameters) > 0 {\n\t\tfor _, p := range item.Meta.Parameters {\n\t\t\tif p.Required {\n\t\t\t\texists := parameters != nil\n\t\t\t\tif exists {\n\t\t\t\t\t_, exists = parameters[p.Name]\n\t\t\t\t}\n","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/goharbor/harbor/blob/7b2fd08cc568955cca339afeefab27372840d936/src/pkg/retention/policy/rule/index/index.go#L167-L203","documentation":"Returned by rule index Valid(templateID, parameters) when the template ID string is empty. Rule templates (always, latestPulledN, daysSinceLastPull, etc.) are registered into an index; Valid rejects the empty key before lookup. It means a retention policy rule carries no template selector.","triggerScenarios":"Creating/updating a retention policy whose rule omits or blanks the template field; policy JSON built programmatically with an unset rule.Template; payload corruption stripping the template key.","commonSituations":"Scripted policy creation missing required fields; Harbor API clients with schema drift after upgrade (template renamed/required); copy-paste from old policy exports.","solutions":["Set a valid template ID on each rule, e.g. 'always', 'latestPushedK', 'daysSinceLastPull'","Validate every rule has a non-empty template before POSTing the policy","Check current template IDs supported by your Harbor version via the retention API metadata endpoint"],"exampleFix":"// before\nif err := index.Valid(\"\", params); err != nil { ... }\n\n// after\nif err := index.Valid(\"always\", params); err != nil { ... }","handlingStrategy":"validation","validationCode":"for _, r := range policy.Rules {\n    if len(r.Template) == 0 {\n        return fmt.Errorf(\"retention rule %v has no template\", r.ID)\n    }\n    if err := ruleindex.Valid(r.Template, r.Params); err != nil {\n        return err\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check rule.template is non-empty for every rule before policy submission","Enumerate supported template IDs from the retention API before building rules","Pin template IDs in code as constants to survive refactors"],"tags":["retention","rule","template","validation"],"backgroundTag":null,"analyzedSha":"7b2fd08cc568955cca339afeefab27372840d936","analyzedAt":"2026-08-16T00:00:10.961Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}