juanfont/headscale · error
reading IPv4 addresses from database: %w
Error message
reading IPv4 addresses from database: %w
What it means
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).
Source
Thrown at hscontrol/db/ip.go:79
) (*IPAllocator, error) {
ret := IPAllocator{
prefix4: prefix4,
prefix6: prefix6,
strategy: strategy,
}
var (
v4s []sql.NullString
v6s []sql.NullString
)
if db != nil {
err := db.Read(func(rx *gorm.DB) error {
return rx.Model(&types.Node{}).Pluck("ipv4", &v4s).Error
})
if err != nil {
return nil, fmt.Errorf("reading IPv4 addresses from database: %w", err)
}
err = db.Read(func(rx *gorm.DB) error {
return rx.Model(&types.Node{}).Pluck("ipv6", &v6s).Error
})
if err != nil {
return nil, fmt.Errorf("reading IPv6 addresses from database: %w", err)
}
}
var ips netipx.IPSetBuilder
// Add network and broadcast addrs to used pool so they
// are not handed out to nodes.
if prefix4 != nil {
network4, broadcast4 := util.GetIPPrefixEndpoints(*prefix4)
ips.Add(network4)
ips.Add(broadcast4)View on GitHub (pinned to 565fd254d0)
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.
Defensive patterns
Strategy: try-catch
Try / catch
// When embedding headscale's db package:
// alloc, err := db.NewIPAllocator(hdb, prefix4, prefix6, strategy)
// if err != nil {
// if strings.Contains(err.Error(), "reading IPv4 addresses") {
// // schema/connection issue: verify migrations ran, then retry once
// }
// return err
// } Prevention
- Always run the latest migrations before constructing the allocator.
- Keep database credentials/connection pools healthy (connection liveness checks).
When it happens
Trigger: 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.
Common situations: Binary/database version mismatch after a botched upgrade; postgres restarted out from under headscale; concurrent long write transaction on sqlite causing busy timeouts.
Related errors
- reading IPv6 addresses from database: %w
- database type not supported
- no IPv4 or IPv6 prefix configured, minimum one prefix is req
- init state: %w
- version check: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/adf40432fe3ce20a.
Report an issue: GitHub.