{"record":{"id":"0aa7905b40504b58","repo":"juanfont/headscale","slug":"parsing-ip-address-from-database-w","errorCode":null,"errorMessage":"parsing IP address from database: %w","messagePattern":"parsing IP address from database: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/db/ip.go","lineNumber":119,"sourceCode":"\t\t// the database into account to start at a more \"educated\" location.\n\t\tret.prev4 = network4\n\t}\n\n\tif prefix6 != nil {\n\t\tnetwork6, broadcast6 := util.GetIPPrefixEndpoints(*prefix6)\n\t\tips.Add(network6)\n\t\tips.Add(broadcast6)\n\n\t\tret.prev6 = network6\n\t}\n\n\t// Fetch all the IP Addresses currently handed out from the Database\n\t// and add them to the used IP set.\n\tfor _, addrStr := range append(v4s, v6s...) {\n\t\tif addrStr.Valid {\n\t\t\taddr, err := netip.ParseAddr(addrStr.String)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parsing IP address from database: %w\", err)\n\t\t\t}\n\n\t\t\tips.Add(addr)\n\t\t}\n\t}\n\n\t// Build the initial IPSet to validate that we can use it.\n\t_, err := ips.IPSet()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\n\t\t\t\"building initial IP Set: %w\",\n\t\t\terr,\n\t\t)\n\t}\n\n\tret.usedIPs = ips\n\n\treturn &ret, nil","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/db/ip.go#L101-L137","documentation":"Each stored node address string from the ipv4/ipv6 columns is parsed with netip.ParseAddr; failure means a stored value is not a valid IP literal. This is a data-corruption error: headscale itself only ever writes parseable addresses, so a bad value implies external modification or a damaged database.","triggerScenarios":"Someone hand-edited nodes.ipv4/ipv6 (e.g. set it to '10.0.0.256', an empty-but-non-null string, or a hostname); a buggy older version or external script wrote malformed values.","commonSituations":"Manual 'fixes' via SQL UPDATE on the nodes table; imports from third-party tooling; encoding corruption in a restored backup.","solutions":["Find the offending rows: SELECT id, hostname, ipv4, ipv6 FROM nodes WHERE ipv4 IS NOT NULL AND ipv4 != '' (inspect values manually).","Correct or NULL out the malformed address and restart headscale.","Never edit node IPs by hand — delete/re-register the node or use the API instead."],"exampleFix":"-- before (corrupt row)\nUPDATE nodes SET ipv4 = '10.0.0.300' WHERE id = 7; -- invalid literal\n\n-- after\nUPDATE nodes SET ipv4 = '10.0.0.7' WHERE id = 7;\n-- or remove and re-register the node","handlingStrategy":"validation","validationCode":"// Audit stored addresses before allocator construction:\n// SELECT id, ipv4, ipv6 FROM nodes\n//   WHERE (ipv4 IS NOT NULL AND ipv4 != '' AND ipv4 NOT GLOB '*.*.*')\n//      OR (ipv6 IS NOT NULL AND ipv6 = '');\n-- Or in Go over the plucked values:\nfunc allParseable(vals []sql.NullString) bool {\n    for _, v := range vals {\n        if v.Valid && v.String != \"\" {\n            if _, err := netip.ParseAddr(v.String); err != nil { return false }\n        }\n    }\n    return true\n}","typeGuard":null,"tryCatchPattern":"// Not retryable — repair data. Locate the malformed row via the audit\n// query, fix or NULL it, then restart. Escalate if rows were written by\n// headscale itself (that would be a bug worth reporting upstream).","preventionTips":["Never hand-write node IP columns with SQL.","Manage node lifecycle through the headscale CLI/API only.","Validate third-party imports that touch the nodes table."],"tags":["database","ip-allocation","data-corruption","parsing"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}