{"record":{"id":"88f0f36af0c4b819","repo":"AdguardTeam/AdGuardHome","slug":"parsing-expiry-time-w","errorCode":null,"errorMessage":"parsing expiry time: %w","messagePattern":"parsing expiry time: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/dhcpd/db.go","lineNumber":79,"sourceCode":"\t\tHostname: l.Hostname,\n\t\tHWAddr:   l.HWAddr.String(),\n\t\tIP:       l.IP,\n\t\tIsStatic: l.IsStatic,\n\t}\n}\n\n// toLease converts *dbLease to *dhcpsvc.Lease.\nfunc (dl *dbLease) toLease() (l *dhcpsvc.Lease, err error) {\n\tmac, err := net.ParseMAC(dl.HWAddr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parsing hardware address: %w\", err)\n\t}\n\n\texpiry := time.Time{}\n\tif !dl.IsStatic {\n\t\texpiry, err = time.Parse(time.RFC3339, dl.Expiry)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"parsing expiry time: %w\", err)\n\t\t}\n\t}\n\n\treturn &dhcpsvc.Lease{\n\t\tExpiry:   expiry,\n\t\tIP:       dl.IP,\n\t\tHostname: dl.Hostname,\n\t\tHWAddr:   mac,\n\t\tIsStatic: dl.IsStatic,\n\t}, nil\n}\n\n// dbLoad loads stored leases.\nfunc (s *server) dbLoad() (err error) {\n\tdata, err := os.ReadFile(s.conf.dbFilePath)\n\tif err != nil {\n\t\tif !errors.Is(err, os.ErrNotExist) {\n\t\t\treturn fmt.Errorf(\"reading db: %w\", err)","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/AdguardTeam/AdGuardHome/blob/b41aefbe51c8dde65e2c50f093996afa0502edf9/internal/dhcpd/db.go#L61-L97","documentation":"A stored dynamic lease's Expiry field is not a valid RFC3339 timestamp, so it cannot be converted to time.Time. Static leases are exempt (expiry skipped when IsStatic is true).","triggerScenarios":"Loading the lease DB where a non-static lease has an empty, truncated, or non-RFC3339 Expiry string (e.g. missing timezone offset).","commonSituations":"Hand-edited lease files, corruption, or a version change in date format; expiry set to 0 or unix epoch integer instead of RFC3339.","solutions":["Fix expiry values to RFC3339 format with timezone (2026-01-02T15:04:05Z)","Remove the leases file to force re-learning of dynamic leases","Check for and fix truncated/corrupted files"],"exampleFix":"// before\n{\"Expiry\": \"2026-01-02 15:04:05\"}\n// after\n{\"Expiry\": \"2026-01-02T15:04:05Z\"}","handlingStrategy":"validation","validationCode":"if !dl.IsStatic {\n    if _, err := time.Parse(time.RFC3339, dl.Expiry); err != nil { /* drop lease */ }\n}","typeGuard":"func isRFC3339(s string) bool { _, err := time.Parse(time.RFC3339, s); return err == nil }","tryCatchPattern":null,"preventionTips":["Use RFC3339 with timezone in any tooling that writes leases","Stop the service before editing the DB"],"tags":["dhcp","leases","time-parsing","persistence"],"backgroundTag":"corrupted-lease-database","analyzedSha":"b41aefbe51c8dde65e2c50f093996afa0502edf9","analyzedAt":"2026-08-27T04:57:55.097Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}