{"record":{"id":"ead366ed6d6b5118","repo":"goharbor/harbor","slug":"bad-request-ead366","errorCode":"BAD_REQUEST","errorMessage":"empty name","messagePattern":"empty name","errorType":"error_code","errorClass":"lib/errors.Error","httpStatus":400,"severity":"error","filePath":"src/controller/replication/model/model.go","lineNumber":67,"sourceCode":"\tCopyByChunk               bool            `json:\"copy_by_chunk\"`\n\tSingleActiveReplication   bool            `json:\"single_active_replication\"`\n}\n\n// IsScheduledTrigger returns true when the policy is scheduled trigger and enabled\nfunc (p *Policy) IsScheduledTrigger() bool {\n\tif !p.Enabled {\n\t\treturn false\n\t}\n\tif p.Trigger == nil {\n\t\treturn false\n\t}\n\treturn p.Trigger.Type == model.TriggerTypeScheduled\n}\n\n// Validate the policy\nfunc (p *Policy) Validate() error {\n\tif len(p.Name) == 0 {\n\t\treturn errors.New(nil).WithCode(errors.BadRequestCode).WithMessage(\"empty name\")\n\t}\n\tvar srcRegistryID, dstRegistryID int64\n\tif p.SrcRegistry != nil {\n\t\tsrcRegistryID = p.SrcRegistry.ID\n\t}\n\tif p.DestRegistry != nil {\n\t\tdstRegistryID = p.DestRegistry.ID\n\t}\n\n\t// one of the source registry and destination registry must be Harbor itself\n\tif srcRegistryID != 0 && dstRegistryID != 0 ||\n\t\tsrcRegistryID == 0 && dstRegistryID == 0 {\n\t\treturn errors.New(nil).WithCode(errors.BadRequestCode).\n\t\t\tWithMessage(\"either src_registry or dest_registry should be empty and the other one shouldn't be empty\")\n\t}\n\n\t// valid the filters\n\tfor _, f := range p.Filters {","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/goharbor/harbor/blob/7b2fd08cc568955cca339afeefab27372840d936/src/controller/replication/model/model.go#L49-L85","documentation":"Policy.Validate (src/controller/replication/model/model.go:67) requires a non-empty Name when creating or updating a replication policy; an empty name is rejected with BAD_REQUEST (HTTP 400). It is the first check in a chain that also validates registries, filters, namespace, and trigger.","triggerScenarios":"POST /api/v2.0/replication/policies or PUT .../policies/{id} with a body whose name field is missing, empty, or only whitespace; building the policy payload programmatically and forgetting to set Name.","commonSituations":"Generated API clients with omitempty dropping an unset name; UI form submitted before the name field was filled; JSON payload hand-edited and the name key removed.","solutions":["Add a non-empty name to the request body and retry","If using a generated client, verify the name field is actually serialized (check for omitempty on an empty string)","Add client-side schema validation requiring name before calling the API"],"exampleFix":"// before\nPOST /api/v2.0/replication/policies\n{\"name\": \"\", \"src_registry\": {\"id\": 1}}  // 400: empty name\n\n// after\n{\"name\": \"prod-to-dr\", \"src_registry\": {\"id\": 1}}","handlingStrategy":"validation","validationCode":"func validatePolicyPayload(p *PolicyPayload) error {\n    if strings.TrimSpace(p.Name) == \"\" {\n        return errors.New(\"policy name is required\")\n    }\n    return nil\n}","typeGuard":"func isEmptyNameErr(err error) bool {\n    return errors.IsErr(err, errors.BadRequestCode) && strings.Contains(err.Error(), \"empty name\")\n}","tryCatchPattern":"if err := policyCtl.Create(ctx, policy); err != nil {\n    if errors.IsErr(err, errors.BadRequestCode) && strings.Contains(err.Error(), \"empty name\") {\n        // surface a form error; do not retry\n    }\n}","preventionTips":["Make name a required field in client schemas and UI forms","Beware omitempty on name in generated structs - an empty string may be dropped or sent","Run policy.Validate() locally when embedding the controller"],"tags":["harbor","replication","policy","validation","bad-request"],"backgroundTag":null,"analyzedSha":"7b2fd08cc568955cca339afeefab27372840d936","analyzedAt":"2026-08-16T00:00:10.961Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}