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

  1. Verify the sqlite database path still exists and is readable after gorm.Open (no concurrent deletion/unmount).
  2. Check earlier log lines for a swallowed gorm.Open warning; fix the connection issue there.
  3. 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

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


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/bdb0c6d9ee8b02f7. Report an issue: GitHub.