juanfont/headscale · error
loading policy from database: %w
Error message
loading policy from database: %w
What it means
Thrown in `headscale policy get` bypass mode when d.GetPolicy() fails on the directly-opened database. The policy row is stored in the database (acl_policy_path must be empty / db mode); the read failed at the SQL layer or no usable policy row exists.
Source
Thrown at cmd/headscale/cli/policy.go:88
}
var getPolicy = &cobra.Command{
Use: "get",
Short: "Print the current ACL Policy",
Aliases: []string{cmdShow, "view", "fetch"},
RunE: func(cmd *cobra.Command, args []string) error {
var policyData string
if bypass, _ := cmd.Flags().GetBool(bypassFlag); bypass {
d, err := openBypassDB(cmd)
if err != nil {
return err
}
defer d.Close()
pol, err := d.GetPolicy()
if err != nil {
return fmt.Errorf("loading policy from database: %w", err)
}
policyData = pol.Data
} else {
err := withClient(func(ctx context.Context, client *clientv1.ClientWithResponses) error {
resp, err := client.GetPolicyWithResponse(ctx)
if err != nil {
return fmt.Errorf("loading ACL policy: %w", err)
}
if resp.StatusCode() != http.StatusOK {
return apiError(resp.StatusCode(), resp.ApplicationproblemJSONDefault)
}
policyData = resp.JSON200.Policy
return nil
})View on GitHub (pinned to 565fd254d0)
Solutions
- Confirm the instance actually runs in database policy mode (acl_policy_path empty in config).
- If file mode was intended, run `policy get` WITHOUT the bypass flag only after switching modes; otherwise read the HuJSON file directly.
- Run `headscale db migrate`-equivalent / check migration status to ensure the policy table exists.
- If a fresh policy is acceptable, use `policy set --bypass...` to seed the row.
Defensive patterns
Strategy: fallback
Try / catch
pol, err := d.GetPolicy()
if err != nil {
if isNoPolicyRow(err) { // sql.ErrNoRows or equivalent
// db policy mode never initialised: seed via policy set, or switch to file mode
}
return err
} Prevention
- Decide policy mode (file vs db) once at setup and keep acl_policy_path consistent with it.
- After major upgrades, run policy get once to confirm the row survives migrations.
- Document which mode your deployment uses — mixed mode is the usual root cause.
When it happens
Trigger: `policy get --bypass...` where the database was never initialized with a policy row, the table is missing (very old database not migrated), or the SQL query errors (locked/corrupt DB).
Common situations: Instance that always used file-based policy (acl_policy_path set) so the db policy table was never populated; interrupted migration leaving the schema half-applied; opening a non-headscale SQLite file by mistake.
Related errors
- loading users for policy validation: %w
- setting ACL policy: %w
- loading users: %w
- loading nodes: %w
- loading config: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/74ba8b33fe7fb6c0.
Report an issue: GitHub.