{"record":{"id":"fa816c52db9be636","repo":"netbirdio/netbird","slug":"record-type-is-required","errorCode":null,"errorMessage":"record type is required","messagePattern":"record type is required","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"management/internals/modules/zones/records/record.go","lineNumber":71,"sourceCode":"\nfunc (r *Record) FromAPIRequest(req *api.DNSRecordRequest) {\n\tr.Name = req.Name\n\tr.Type = RecordType(req.Type)\n\tr.Content = req.Content\n\tr.TTL = req.Ttl\n}\n\nfunc (r *Record) Validate() error {\n\tif r.Name == \"\" {\n\t\treturn errors.New(\"record name is required\")\n\t}\n\n\tif !domain.IsValidDomain(r.Name) {\n\t\treturn errors.New(\"invalid record name format\")\n\t}\n\n\tif r.Type == \"\" {\n\t\treturn errors.New(\"record type is required\")\n\t}\n\n\tswitch r.Type {\n\tcase RecordTypeA:\n\t\tif err := validateIPv4(r.Content); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase RecordTypeAAAA:\n\t\tif err := validateIPv6(r.Content); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase RecordTypeCNAME:\n\t\tif !domain.IsValidDomainNoWildcard(r.Content) {\n\t\t\treturn errors.New(\"invalid CNAME target format\")\n\t\t}\n\tdefault:\n\t\treturn errors.New(\"invalid record type, must be A, AAAA, or CNAME\")\n\t}","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/management/internals/modules/zones/records/record.go#L53-L89","documentation":"Validate() returns this when Type is the empty string. FromAPIRequest copies req.Type (api.DNSRecordType, a string enum), so an omitted or blank \"type\" in DNSRecordRequest reaches it. The empty check runs before the per-type content switch.","triggerScenarios":"Record create/update body without a \"type\" key, or {\"type\": \"\"}; building a Record literal where Type is left as its zero value.","commonSituations":"A UI that defaults the type field to empty until a dropdown is touched; partial JSON merges that drop the type field; clients that send the type under a different key name.","solutions":["Set \"type\" to one of \"A\", \"AAAA\", or \"CNAME\" in the request.","If building Record in Go, set r.Type = records.RecordTypeA (or AAAA/CNAME) before Validate().","Make the client's type field required at the form/boundary layer so the request never leaves without it."],"exampleFix":"// before\n{\"name\": \"api\", \"type\": \"\", \"content\": \"192.0.2.1\"}\n// after\n{\"name\": \"api\", \"type\": \"A\", \"content\": \"192.0.2.1\"}","handlingStrategy":"validation","validationCode":"if req.Type == \"\" {\n    return fmt.Errorf(\"record type is required\")\n}","typeGuard":"func hasRecordType(req *api.DNSRecordRequest) bool {\n    return string(req.Type) != \"\"\n}","tryCatchPattern":"if err := rec.Validate(); err != nil {\n    return respondBadRequest(err)\n}","preventionTips":["Use a dropdown limited to A/AAAA/CNAME instead of free text for the type field.","Fail client-side when type is empty rather than sending the request."],"tags":["dns","zones","validation","management-api"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}