juanfont/headscale · error
opening database: %w
Error message
opening database: %w
What it means
Thrown by bypassDatabase() when db.NewHeadscaleDatabase() cannot open the configured database. Bypass mode skips the running server and attaches directly to SQLite/PostgreSQL; failure means the connection string, driver, permissions, or schema state prevented a usable handle.
Source
Thrown at cmd/headscale/cli/policy.go:34
)
const (
bypassFlag = "bypass-server-and-access-database-directly" //nolint:gosec // not a credential
)
var errAborted = errors.New("command aborted by user")
// bypassDatabase opens the database directly, bypassing the running server.
// The caller must close the returned handle.
func bypassDatabase() (*db.HSDatabase, error) {
cfg, err := types.LoadServerConfig()
if err != nil {
return nil, fmt.Errorf("loading config: %w", err)
}
d, err := db.NewHeadscaleDatabase(cfg)
if err != nil {
return nil, fmt.Errorf("opening database: %w", err)
}
return d, nil
}
// openBypassDB confirms the destructive bypass action and opens the database
// directly. The caller is responsible for closing the returned handle.
func openBypassDB(cmd *cobra.Command) (*db.HSDatabase, error) {
if !confirmAction(cmd, "DO NOT run this command if an instance of headscale is running, are you sure headscale is not running?") {
return nil, errAborted
}
return bypassDatabase()
}
func init() {
rootCmd.AddCommand(policyCmd)
View on GitHub (pinned to 565fd254d0)
Solutions
- Ensure headscale is NOT running (the command warns about this for a reason): `systemctl stop headscale` first.
- Check the database.path / postgres settings in the config the CLI just loaded.
- Verify file permissions on the SQLite file for the user running the CLI.
- For PostgreSQL, test connectivity with psql using the same DSN.
Example fix
# before: bypass while server is running (SQLite locked) headscale policy set --file p.hujson --bypass-server-and-access-database-directly # after sudo systemctl stop headscale headscale policy set --file p.hujson --bypass-server-and-access-database-directly sudo systemctl start headscale
Defensive patterns
Strategy: validation
Validate before calling
// refuse to bypass while the server holds the database (SQLite case)
func ensureHeadscaleStopped(dbPath string) error {
if err := exec.Command("fuser", dbPath).Run(); err == nil {
return fmt.Errorf("another process holds %s — stop headscale first", dbPath)
}
return nil
} Try / catch
d, err := bypassDatabase()
if err != nil {
if strings.Contains(err.Error(), "opening database") {
// likely lock/permissions: stop service, fix perms, retry once
}
return err
} Prevention
- Stop the headscale service before any --bypass command; script it as stop -> command -> start.
- Keep database file ownership consistent with the user running the CLI.
- For PostgreSQL, verify the DSN with psql before automation uses bypass mode.
When it happens
Trigger: `policy get/set/check --bypass...` with a SQLite file that does not exist or is not writable; PostgreSQL refused connection (wrong host/port/credentials); database file locked by a running headscale instance; migrations failing to apply on open.
Common situations: Running bypass against a database while headscale is running (SQLite lock); config's database section pointing at a stale path; Postgres TLS/auth mismatch; read-only filesystem (container, snap).
Related errors
- loading config: %w
- loading policy from database: %w
- loading users for policy validation: %w
- setting ACL policy: %w
- loading users: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/d6716744fb4d076d.
Report an issue: GitHub.