juanfont/headscale · error
getting DB from gorm: %w
Error message
getting DB from gorm: %w
What it means
headscale extracts the underlying *sql.DB from the GORM handle (dbConn.DB()) to run squibble schema validation on sqlite. This error means GORM could not return its raw database handle — which effectively only happens when the GORM handle is uninitialized or the underlying connection pool is already closed/invalid.
Source
Thrown at hscontrol/db/db.go:1018
currentVersion := types.GetVersionInfo().Version
if !isDev(currentVersion) {
err = setDatabaseVersion(dbConn, currentVersion)
if err != nil {
return nil, fmt.Errorf(
"storing database version: %w",
err,
)
}
}
// Validate that the schema ends up in the expected state.
// This is currently only done on sqlite as squibble does not
// support Postgres and we use our sqlite schema as our source of
// truth.
if cfg.Database.Type == types.DatabaseSqlite {
sqlConn, err := dbConn.DB()
if err != nil {
return nil, fmt.Errorf("getting DB from gorm: %w", err)
}
// or else it blocks...
sqlConn.SetMaxIdleConns(maxIdleConns)
sqlConn.SetMaxOpenConns(maxOpenConns)
defer sqlConn.SetMaxIdleConns(1)
defer sqlConn.SetMaxOpenConns(1)
ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
defer cancel()
opts := squibble.DigestOptions{
IgnoreTables: []string{
// Litestream tables, these are inserted by
// litestream and not part of our schema
// https://litestream.io/how-it-works
"_litestream_lock",View on GitHub (pinned to 565fd254d0)
Solutions
- Verify the sqlite database path still exists and is readable after gorm.Open (no concurrent deletion/unmount).
- Check earlier log lines for a swallowed gorm.Open warning; fix the connection issue there.
- Re-run startup — this is a transient-handle symptom, not a schema problem.
Defensive patterns
Strategy: retry
Try / catch
// Retry by restarting: if gorm.Open partially failed, the earlier error is
// the real cause. If using the db package programmatically:
// sqlDB, err := gormDB.DB()
// if err != nil { return fmt.Errorf("getting DB from gorm: %w", err) }
// treat as connection-layer failure, re-dial. Prevention
- Check gorm.Open's error immediately and fail fast.
- Keep the sqlite file on stable local storage.
When it happens
Trigger: cfg.Database.Type == sqlite and dbConn.DB() returns an error: the gorm.DB was built against a Dialector that failed silently during Open, or the pool was closed between Open and this call.
Common situations: Almost never seen in practice because gorm.Open errors earlier; can appear if the sqlite file becomes inaccessible immediately after Open (deleted file, unmounted volume) or with exotic embedded setups.
Related errors
- saving API key to database: %w
- automigrating types.Route: %w
- automigrating types.Node: %w
- adding prefix column: %w
- checking name uniqueness: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/bdb0c6d9ee8b02f7.
Report an issue: GitHub.