{"record":{"id":"89a2f79d5b371f9b","repo":"AdguardTeam/AdGuardHome","slug":"decoding-hex-w","errorCode":null,"errorMessage":"decoding hex: %w","messagePattern":"decoding hex: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/dhcpd/options_unix.go","lineNumber":40,"sourceCode":"// TODO(e.burkov):  Add an option for classless routes.\nconst (\n\ttypDel  = \"del\"\n\ttypBool = \"bool\"\n\ttypDur  = \"dur\"\n\ttypHex  = \"hex\"\n\ttypIP   = \"ip\"\n\ttypIPs  = \"ips\"\n\ttypText = \"text\"\n\ttypU8   = \"u8\"\n\ttypU16  = \"u16\"\n)\n\n// parseDHCPOptionHex parses a DHCP option as a hex-encoded string.\nfunc parseDHCPOptionHex(s string) (val dhcpv4.OptionValue, err error) {\n\tvar data []byte\n\tdata, err = hex.DecodeString(s)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"decoding hex: %w\", err)\n\t}\n\n\treturn dhcpv4.OptionGeneric{Data: data}, nil\n}\n\n// parseDHCPOptionIP parses a DHCP option as a single IP address.\nfunc parseDHCPOptionIP(s string) (val dhcpv4.OptionValue, err error) {\n\tvar ip net.IP\n\t// All DHCPv4 options require IPv4, so don't put the 16-byte version.\n\t// Otherwise, the clients will receive weird data that looks like four IPv4\n\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","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/AdguardTeam/AdGuardHome/blob/b41aefbe51c8dde65e2c50f093996afa0502edf9/internal/dhcpd/options_unix.go#L22-L58","documentation":"Returned by parseDHCPOptionHex when a DHCP option declared with type hex fails hex.DecodeString. Only characters 0-9, a-f, A-F in even count are accepted. This occurs while parsing the custom DHCP options list in the config.","triggerScenarios":"Setting an option like 'hex:myhexvalue' where the value contains non-hex characters, an odd number of digits, or a 0x prefix ('0xdeadbeef' is rejected).","commonSituations":"Copying option values from vendor docs that include 0x prefixes or spaces; deleting one hex digit by accident; pasting values with colons (AA:BB style).","solutions":["Strip any 0x prefix and colons/spaces, keep only 0-9a-f, ensure even length","Validate with a quick check: echo -n 'VALUE' | wc -c (must be even) and echo 'VALUE' | grep -E '^[0-9a-fA-F]+$'","Use lowercase or uppercase consistently; both are accepted"],"exampleFix":"// before\n\"options\":[{\"code\":66,\"type\":\"hex\",\"value\":\"0x68656c6c6f\"}]\n// -> decoding hex: encoding/hex: invalid byte: U+0078 'x'\n\n// after\n\"options\":[{\"code\":66,\"type\":\"hex\",\"value\":\"68656c6c6f\"}]","handlingStrategy":"validation","validationCode":"func hexOK(s string) bool {\n\tif len(s)%2 != 0 { return false }\n\t_, err := hex.DecodeString(strings.TrimPrefix(strings.TrimPrefix(s, \"0x\"), \"0X\"))\n\treturn err == nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := hex.DecodeString(s); err != nil {\n    s = strings.TrimPrefix(s, \"0x\") // retry after stripping common prefix\n    // or reject the config before submission\n}","preventionTips":["Strip 0x prefixes and colons from vendor values","Keep hex strings even-length","Lint option values with a hex decoder before saving"],"tags":["dhcp","options","hex","validation","configuration"],"backgroundTag":"invalid-hex-string","analyzedSha":"b41aefbe51c8dde65e2c50f093996afa0502edf9","analyzedAt":"2026-08-27T04:57:55.097Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}