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

  1. Confirm the instance actually runs in database policy mode (acl_policy_path empty in config).
  2. If file mode was intended, run `policy get` WITHOUT the bypass flag only after switching modes; otherwise read the HuJSON file directly.
  3. Run `headscale db migrate`-equivalent / check migration status to ensure the policy table exists.
  4. 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

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


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