{"record":{"id":"adf40432fe3ce20a","repo":"juanfont/headscale","slug":"reading-ipv4-addresses-from-database-w","errorCode":null,"errorMessage":"reading IPv4 addresses from database: %w","messagePattern":"reading IPv4 addresses from database: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/db/ip.go","lineNumber":79,"sourceCode":") (*IPAllocator, error) {\n\tret := IPAllocator{\n\t\tprefix4: prefix4,\n\t\tprefix6: prefix6,\n\n\t\tstrategy: strategy,\n\t}\n\n\tvar (\n\t\tv4s []sql.NullString\n\t\tv6s []sql.NullString\n\t)\n\n\tif db != nil {\n\t\terr := db.Read(func(rx *gorm.DB) error {\n\t\t\treturn rx.Model(&types.Node{}).Pluck(\"ipv4\", &v4s).Error\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"reading IPv4 addresses from database: %w\", err)\n\t\t}\n\n\t\terr = db.Read(func(rx *gorm.DB) error {\n\t\t\treturn rx.Model(&types.Node{}).Pluck(\"ipv6\", &v6s).Error\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"reading IPv6 addresses from database: %w\", err)\n\t\t}\n\t}\n\n\tvar ips netipx.IPSetBuilder\n\n\t// Add network and broadcast addrs to used pool so they\n\t// are not handed out to nodes.\n\tif prefix4 != nil {\n\t\tnetwork4, broadcast4 := util.GetIPPrefixEndpoints(*prefix4)\n\t\tips.Add(network4)\n\t\tips.Add(broadcast4)","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/db/ip.go#L61-L97","documentation":"The IP allocator constructor builds the set of already-used IPv4 addresses by plucking the ipv4 column of every node row (inside a read transaction via db.Read). This error means that SELECT failed — connection problems, or the nodes table missing the ipv4 column (schema drift).","triggerScenarios":"NewIPAllocator called at startup or during IP backfill when the nodes.ipv4 column does not exist (DB schema older than the code expects), the database connection is broken, or a read lock blocks the query on sqlite.","commonSituations":"Binary/database version mismatch after a botched upgrade; postgres restarted out from under headscale; concurrent long write transaction on sqlite causing busy timeouts.","solutions":["Verify schema and binary versions match — run the newest headscale once so migrations add any missing columns.","Check DB connectivity and restart postgres if needed; retry startup.","On sqlite, remove concurrent writers so reads are not blocked."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// When embedding headscale's db package:\n//   alloc, err := db.NewIPAllocator(hdb, prefix4, prefix6, strategy)\n//   if err != nil {\n//       if strings.Contains(err.Error(), \"reading IPv4 addresses\") {\n//           // schema/connection issue: verify migrations ran, then retry once\n//       }\n//       return err\n//   }","preventionTips":["Always run the latest migrations before constructing the allocator.","Keep database credentials/connection pools healthy (connection liveness checks)."],"tags":["database","ip-allocation","ipv4","startup"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}