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
- Stop the headscale service before bypass commands.
- Verify with the non-bypass path (`headscale users list`) once the server is up — if that also fails, the database itself needs attention.
- Check SQLite integrity: `sqlite3 <db> 'PRAGMA integrity_check;'`.
- 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
- Always stop headscale before bypass commands — encode as a wrapper script.
- Alert on 'database is locked' in logs; it indicates concurrent access.
- Snapshot the database before bypass writes.
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
- loading policy from database: %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/450dab6729a3b7ff.
Report an issue: GitHub.