{"record":{"id":"ad4b84130a893d75","repo":"AdguardTeam/AdGuardHome","slug":"decoding-dur-w","errorCode":null,"errorMessage":"decoding dur: %w","messagePattern":"decoding dur: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/dhcpd/options_unix.go","lineNumber":84,"sourceCode":"\tfor i, ipStr := range strings.Split(s, \",\") {\n\t\tip, err = parseDHCPOptionIP(ipStr)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"parsing ip at index %d: %w\", i, err)\n\t\t}\n\n\t\tips = append(ips, net.IP(ip.(dhcpv4.IP)))\n\t}\n\n\treturn ips, nil\n}\n\n// parseDHCPOptionDur parses a DHCP option as a duration in a human-readable\n// form.\nfunc parseDHCPOptionDur(s string) (val dhcpv4.OptionValue, err error) {\n\tvar v timeutil.Duration\n\terr = v.UnmarshalText([]byte(s))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"decoding dur: %w\", err)\n\t}\n\n\treturn dhcpv4.Duration(v), nil\n}\n\n// parseDHCPOptionUint parses a DHCP option as an unsigned integer.  bitSize is\n// expected to be 8 or 16.\nfunc parseDHCPOptionUint(s string, bitSize int) (val dhcpv4.OptionValue, err error) {\n\tvar v uint64\n\tv, err = strconv.ParseUint(s, 10, bitSize)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"decoding u%d: %w\", bitSize, err)\n\t}\n\n\tswitch bitSize {\n\tcase 8:\n\t\treturn dhcpv4.OptionGeneric{Data: []byte{uint8(v)}}, nil\n\tcase 16:","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/AdguardTeam/AdGuardHome/blob/b41aefbe51c8dde65e2c50f093996afa0502edf9/internal/dhcpd/options_unix.go#L66-L102","documentation":"Returned by parseDHCPOptionDur when a DHCP option typed as duration fails timeutil.Duration.UnmarshalText. Accepts human-readable forms like '1h30m' or Go duration syntax, and rejects plain numbers without units (unless a unit is implied by the specific format) and garbage strings.","triggerScenarios":"Setting an option (e.g. lease time or renew timers) with value '3600' instead of '1h' or '3600s', or with typos like '1hr', '30seconds'.","commonSituations":"Assuming seconds as a raw number is accepted; copying '1h' vs '1 h' spacing differences; vendor docs quoting lease times in bare seconds.","solutions":["Use Go-style duration strings with explicit units: '3600s', '1h', '1h30m'","Spell units exactly (s, m, h, d where supported) without spaces between number and unit","Test parseability locally: go run - <<<'package main;import(\"fmt\";\"time\");func main(){_,e:=time.ParseDuration(\"1h\");fmt.Println(e)}'"],"exampleFix":"// before\n\"value\":\"3600\"\n// -> decoding dur: time: invalid duration \"3600\" (missing unit)\n\n// after\n\"value\":\"3600s\"","handlingStrategy":"validation","validationCode":"func durOK(s string) bool {\n\t_, err := time.ParseDuration(s) // close proxy for timeutil.Duration\n\treturn err == nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always attach a unit: '3600s' not '3600'","Use Go duration syntax (h, m, s)","Test-parse durations in your config pipeline"],"tags":["dhcp","options","duration","validation","configuration"],"backgroundTag":"invalid-duration-format","analyzedSha":"b41aefbe51c8dde65e2c50f093996afa0502edf9","analyzedAt":"2026-08-27T04:57:55.097Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}