{"record":{"id":"58e71f3efaf91c84","repo":"netbirdio/netbird","slug":"ttl-cannot-be-negative","errorCode":null,"errorMessage":"TTL cannot be negative","messagePattern":"TTL cannot be negative","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"management/internals/modules/zones/records/record.go","lineNumber":92,"sourceCode":"\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}\n\n\tif r.TTL < 0 {\n\t\treturn errors.New(\"TTL cannot be negative\")\n\t}\n\n\treturn nil\n}\n\nfunc (r *Record) EventMeta(zoneID, zoneName string) map[string]any {\n\treturn map[string]any{\n\t\t\"name\":      r.Name,\n\t\t\"type\":      string(r.Type),\n\t\t\"content\":   r.Content,\n\t\t\"ttl\":       r.TTL,\n\t\t\"zone_id\":   zoneID,\n\t\t\"zone_name\": zoneName,\n\t}\n}\n\nfunc validateIPv4(content string) error {\n\tif content == \"\" {","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/management/internals/modules/zones/records/record.go#L74-L110","documentation":"Validate() rejects a negative TTL. TTL is copied verbatim from api.DNSRecordRequest.Ttl, and zero is explicitly allowed (it means \"use default\"), so only values below zero trigger this. There is no upper-bound check here.","triggerScenarios":"A record body with \"ttl\": -1; a client computing TTL as a delta (e.g. expiry minus now) that goes negative when the expiry is in the past; integer underflow in generated payloads.","commonSituations":"Scripts deriving TTL from timestamps; form fields accepting negative numbers; negative values used as a \"disable\" sentinel by client convention but rejected by the server.","solutions":["Send a TTL of 0 (default) or a positive number of seconds.","If computing TTL dynamically, clamp it: ttl = max(0, computed).","Validate at the client boundary that the TTL field is >= 0 before issuing the request."],"exampleFix":"// before\n{\"name\": \"api\", \"type\": \"A\", \"content\": \"192.0.2.1\", \"ttl\": -300}\n// after\n{\"name\": \"api\", \"type\": \"A\", \"content\": \"192.0.2.1\", \"ttl\": 300}","handlingStrategy":"validation","validationCode":"if req.Ttl < 0 {\n    return fmt.Errorf(\"ttl must be >= 0, got %d\", req.Ttl)\n}","typeGuard":null,"tryCatchPattern":"if err := rec.Validate(); err != nil {\n    return respondBadRequest(err)\n}","preventionTips":["Clamp computed TTLs with max(0, v) before sending.","Use 0 to mean default rather than a negative sentinel."],"tags":["dns","zones","validation","ttl"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}