juanfont/headscale · critical · errVersionMajorChange

headscale version %s cannot be used with a database last use

Error message

headscale version %s cannot be used with a database last used by %s: %w

What it means

The version compatibility check found that the database was last written by a headscale release with a different MAJOR version than the running binary. Major versions imply breaking schema changes, so headscale refuses to touch the database. The sentinel errVersionMajorChange is wrapped for programmatic detection.

Source

Thrown at hscontrol/db/versioncheck.go:239

	}

	// Previous run was an unversioned build — no meaningful comparison.
	if isDev(storedVersion) {
		return nil
	}

	current, err := parseVersion(currentVersion)
	if err != nil {
		return fmt.Errorf("parsing current version: %w", err)
	}

	stored, err := parseVersion(storedVersion)
	if err != nil {
		return fmt.Errorf("parsing stored database version: %w", err)
	}

	if current.Major != stored.Major {
		return fmt.Errorf(
			"headscale version %s cannot be used with a database last used by %s: %w",
			currentVersion, storedVersion, errVersionMajorChange,
		)
	}

	minorDiff := current.Minor - stored.Minor

	switch {
	case minorDiff == 0:
		// Same minor version — patch changes are always fine.
		return nil

	case minorDiff == 1:
		// Single minor version upgrade — allowed.
		return nil

	case minorDiff > 1:
		// Multi-minor upgrade — blocked.

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Check the stored version (shown in the message) and the running binary version (headscale version).
  2. Follow the release notes for the major upgrade path — typically export/migrate data or start from the intermediate major version.
  3. If the database is disposable (test/dev), delete or move the SQLite file (or drop the Postgres database) and let headscale recreate it.
  4. Never hand-edit the stored version row to force compatibility.
Defensive patterns

Strategy: validation

Validate before calling

// Before starting, compare versions if you can read the stored version:
// (simplified; headscale does this internally at startup)
if current.Major != stored.Major {
    return fmt.Errorf("refusing to run: database is from major version %d, binary is %d", stored.Major, current.Major)
}

Try / catch

if err := db.CheckVersion(...); err != nil {
    if errors.Is(err, errVersionMajorChange) {
        // hard stop: export/migrate via the documented major-upgrade path or start fresh
        log.Fatal().Err(err).Msg("incompatible database major version")
    }
}

Prevention

When it happens

Trigger: Starting a headscale vN binary against a database whose stored version has major != N — e.g. after a major release upgrade or downgrade, or pointing dns/db config at a database file from another headscale major version.

Common situations: Upgrading headscale across a major version boundary in one step; restoring a backup made by a different major version; two installations sharing one database file.

Related errors


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