juanfont/headscale · error

loading users for policy validation: %w

Error message

loading users for policy validation: %w

What it means

Thrown in `headscale policy set --bypass...` when d.ListUsers(nil) fails after opening the database directly. Users are loaded so policy.NewPolicyManager can validate that every user referenced in the HuJSON exists. Failure is at the database read layer, before policy parsing.

Source

Thrown at cmd/headscale/cli/policy.go:144

	Aliases: []string{"put", "update"},
	RunE: func(cmd *cobra.Command, args []string) error {
		policyPath, _ := cmd.Flags().GetString("file")

		policyBytes, err := os.ReadFile(policyPath)
		if err != nil {
			return fmt.Errorf("reading policy file: %w", err)
		}

		if bypass, _ := cmd.Flags().GetBool(bypassFlag); bypass {
			d, err := openBypassDB(cmd)
			if err != nil {
				return err
			}
			defer d.Close()

			users, err := d.ListUsers(nil)
			if err != nil {
				return fmt.Errorf("loading users for policy validation: %w", err)
			}

			_, err = policy.NewPolicyManager(policyBytes, users, views.Slice[types.NodeView]{})
			if err != nil {
				return fmt.Errorf("parsing policy file: %w", err)
			}

			_, err = d.SetPolicy(string(policyBytes))
			if err != nil {
				return fmt.Errorf("setting ACL policy: %w", err)
			}
		} else {
			policyStr := string(policyBytes)

			err := withClient(func(ctx context.Context, client *clientv1.ClientWithResponses) error {
				resp, err := client.SetPolicyWithResponse(ctx, clientv1.SetPolicyJSONRequestBody{
					Policy: &policyStr,
				})

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Stop the headscale service before bypass commands.
  2. Verify with the non-bypass path (`headscale users list`) once the server is up — if that also fails, the database itself needs attention.
  3. Check SQLite integrity: `sqlite3 <db> 'PRAGMA integrity_check;'`.
  4. Restore from backup if corruption is confirmed.
Defensive patterns

Strategy: validation

Try / catch

users, err := d.ListUsers(nil)
if err != nil {
    if isLocked(err) { // 'database is locked' in wrapped text
        // stop headscale, retry once
    }
    return err
}

Prevention

When it happens

Trigger: Bypass-mode set where the users table query fails: corrupt database, concurrent writer holding a lock (headscale still running), or schema mismatch after a partial migration.

Common situations: Forgetting to stop headscale before bypass operations; database file copied/restored mid-migration; running bypass against a file with wrong ownership.

Related errors


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