juanfont/headscale · critical

checking if table %s exists: %w

Error message

checking if table %s exists: %w

What it means

The SQLite schema-recreation migration failed querying sqlite_master to test whether a table (users, pre_auth_keys, api_keys, nodes, policies) exists before renaming it. A bare SELECT from sqlite_master failing indicates a broken connection, corrupted database file, or a non-SQLite database reaching SQLite-only code.

Source

Thrown at hscontrol/db/db.go:293

						"idx_users_deleted_at",
						"idx_provider_identifier",
						"idx_name_provider_identifier",
						"idx_name_no_provider_identifier",
						"idx_api_keys_prefix",
						"idx_policies_deleted_at",
					}

					for _, index := range indexesToDrop {
						_ = tx.Exec("DROP INDEX IF EXISTS " + index).Error
					}

					for _, table := range tablesToRename {
						// Check if table exists before renaming
						var exists bool

						err := tx.Raw("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?", table).Row().Scan(&exists)
						if err != nil {
							return fmt.Errorf("checking if table %s exists: %w", table, err)
						}

						if exists {
							// Drop old table if it exists from previous failed migration
							_ = tx.Exec("DROP TABLE IF EXISTS " + table + "_old").Error

							// Rename current table to _old
							err := tx.Exec("ALTER TABLE " + table + " RENAME TO " + table + "_old").Error
							if err != nil {
								return fmt.Errorf("renaming table %s to %s_old: %w", table, table, err)
							}
						}
					}

					// Create new tables with correct schema
					tableCreationSQL := []string{
						`CREATE TABLE users(
  id integer PRIMARY KEY AUTOINCREMENT,

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Run 'sqlite3 headscale.db "PRAGMA integrity_check;"' to detect corruption.
  2. If corrupt, restore from backup - do not continue migrating a damaged file.
  3. Verify the database configuration (type, path) matches the actual file.
  4. If healthy, retry with exclusive access and check dmesg/logs for disk errors.
Defensive patterns

Strategy: try-catch

Validate before calling

-- Run before upgrading: confirm the file is a healthy SQLite database
PRAGMA integrity_check;
-- expect: ok

Try / catch

if _, err := db.NewHeadscaleDatabase(cfg); err != nil {
    if strings.Contains(err.Error(), "checking if table") {
        // sqlite_master unreadable: corruption or wrong db.type config - restore backup
    }
}

Prevention

When it happens

Trigger: The Raw('SELECT COUNT(*) FROM sqlite_master ...').Row().Scan fails due to a dropped connection, a corrupted SQLite file (run PRAGMA integrity_check), or a misconfigured database type where the SQLite migration runs against another backend.

Common situations: Database file corrupted by power loss or forced kill during an earlier write; headscale.cfg pointing db.type to sqlite while the file is actually a Postgres dump or vice versa; disk errors.

Related errors


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