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
- Check the stored version (shown in the message) and the running binary version (headscale version).
- Follow the release notes for the major upgrade path — typically export/migrate data or start from the intermediate major version.
- If the database is disposable (test/dev), delete or move the SQLite file (or drop the Postgres database) and let headscale recreate it.
- 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
- Back up the database before any headscale upgrade.
- Pin versions in deployment tooling and read release notes before major bumps.
- Never share one database between installations of different major versions.
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
- version check: %w
- headscale version %s cannot be used with a database last use
- headscale version %s cannot be used with a database last use
- automigrating types.Route: %w
- checking if table %s exists: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/f28091ee5c2595c4.
Report an issue: GitHub.