{"record":{"id":"9f80f776cf881125","repo":"AdguardTeam/AdGuardHome","slug":"parsing-ip-at-index-d-w","errorCode":null,"errorMessage":"parsing ip at index %d: %w","messagePattern":"parsing ip at index (.+?): %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/dhcpd/options_unix.go","lineNumber":69,"sourceCode":"\t// addresses.\n\t//\n\t// See https://github.com/AdguardTeam/AdGuardHome/issues/2688.\n\tif ip, err = netutil.ParseIPv4(s); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn dhcpv4.IP(ip), nil\n}\n\n// parseDHCPOptionIPs parses a DHCP option as a comma-separates list of IP\n// addresses.\nfunc parseDHCPOptionIPs(s string) (val dhcpv4.OptionValue, err error) {\n\tvar ips dhcpv4.IPs\n\tvar ip dhcpv4.OptionValue\n\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","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/AdguardTeam/AdGuardHome/blob/b41aefbe51c8dde65e2c50f093996afa0502edf9/internal/dhcpd/options_unix.go#L51-L87","documentation":"Returned by parseDHCPOptionIPs when one element of a comma-separated IP list fails to parse as an IPv4 address. The index (%d) in the message identifies which list position is bad. Used for options taking multiple IPs, like NTP or DNS servers.","triggerScenarios":"Submitting an option of type ip with value \"192.168.1.1, 192.168.1.2\" where any element is empty, has trailing text, is a hostname instead of an IP, or — notably — contains a space after the comma that fails parsing, or includes an IPv6 literal in an IPv4-only option.","commonSituations":"Whitespace after commas; accidentally pasting a DNS name; a trailing comma producing an empty last element; mixing IPv6 into an IPv4 list.","solutions":["Use the reported index to find the exact element: the message says 'parsing ip at index N'","Remove spaces after commas and any trailing comma","Ensure every element is a literal IPv4 dotted-quad (no hostnames, no IPv6)","Validate each element client-side with net.ParseIP/ParseCIDR logic before submitting"],"exampleFix":"// before\n\"value\":\"192.168.1.1, 192.168.1.2,\"  // space + trailing comma\n// -> parsing ip at index 1: ...\n\n// after\n\"value\":\"192.168.1.1,192.168.1.2\"","handlingStrategy":"validation","validationCode":"func ipsOK(s string) bool {\n\tfor i, part := range strings.Split(s, \",\") {\n\t\tif ip := net.ParseIP(strings.TrimSpace(part)); ip == nil || ip.To4() == nil {\n\t\t\t_ = i\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}","typeGuard":null,"tryCatchPattern":"if err := setOption(...); err != nil {\n    if m := regexp.MustCompile(`parsing ip at index (\\d+)`).FindStringSubmatch(err.Error()); m != nil {\n        // fix element at index m[1]\n    }\n}","preventionTips":["No spaces after commas in IP lists","No trailing commas (empty element fails)","Use only IPv4 literals in IPv4 option lists"],"tags":["dhcp","options","ipv4","validation","configuration"],"backgroundTag":"invalid-ip-address","analyzedSha":"b41aefbe51c8dde65e2c50f093996afa0502edf9","analyzedAt":"2026-08-27T04:57:55.097Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}