AdguardTeam/AdGuardHome · error
decoding hex: %w
Error message
decoding hex: %w
What it means
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.
Source
Thrown at internal/dhcpd/options_unix.go:40
// TODO(e.burkov): Add an option for classless routes.
const (
typDel = "del"
typBool = "bool"
typDur = "dur"
typHex = "hex"
typIP = "ip"
typIPs = "ips"
typText = "text"
typU8 = "u8"
typU16 = "u16"
)
// parseDHCPOptionHex parses a DHCP option as a hex-encoded string.
func parseDHCPOptionHex(s string) (val dhcpv4.OptionValue, err error) {
var data []byte
data, err = hex.DecodeString(s)
if err != nil {
return nil, fmt.Errorf("decoding hex: %w", err)
}
return dhcpv4.OptionGeneric{Data: data}, nil
}
// parseDHCPOptionIP parses a DHCP option as a single IP address.
func parseDHCPOptionIP(s string) (val dhcpv4.OptionValue, err error) {
var ip net.IP
// All DHCPv4 options require IPv4, so don't put the 16-byte version.
// Otherwise, the clients will receive weird data that looks like four IPv4
// addresses.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2688.
if ip, err = netutil.ParseIPv4(s); err != nil {
return nil, err
}
return dhcpv4.IP(ip), nilView on GitHub (pinned to b41aefbe51)
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
Example fix
// before
"options":[{"code":66,"type":"hex","value":"0x68656c6c6f"}]
// -> decoding hex: encoding/hex: invalid byte: U+0078 'x'
// after
"options":[{"code":66,"type":"hex","value":"68656c6c6f"}] Defensive patterns
Strategy: validation
Validate before calling
func hexOK(s string) bool {
if len(s)%2 != 0 { return false }
_, err := hex.DecodeString(strings.TrimPrefix(strings.TrimPrefix(s, "0x"), "0X"))
return err == nil
} Try / catch
if _, err := hex.DecodeString(s); err != nil {
s = strings.TrimPrefix(s, "0x") // retry after stripping common prefix
// or reject the config before submission
} Prevention
- Strip 0x prefixes and colons from vendor values
- Keep hex strings even-length
- Lint option values with a hex decoder before saving
When it happens
Trigger: 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).
Common situations: 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).
Related errors
AI-assisted analysis of AdguardTeam/AdGuardHome@b41aefbe51 (2026-08-27).
Data as JSON: /api/errors/89a2f79d5b371f9b.
Report an issue: GitHub.