juanfont/headscale · error

setting ACL policy: %w

Error message

setting ACL policy: %w

What it means

Thrown in `headscale policy set --bypass...` when d.SetPolicy() fails writing the validated policy into the database. Because the CLI already ran NewPolicyManager successfully, this is a persistence-layer failure (write/transaction), not a policy-content failure.

Source

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

			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,
				})
				if err != nil {
					return fmt.Errorf("setting ACL policy: %w", err)
				}

				if resp.StatusCode() != http.StatusOK {
					return apiError(resp.StatusCode(), resp.ApplicationproblemJSONDefault)
				}

				return nil
			})

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Confirm headscale is stopped and no other process holds the database.
  2. Check disk space on the database volume.
  3. Retry the command — the write is a single transaction and idempotent.
  4. If it persists, inspect SQLite lock state (`fuser <db>`, `lsof <db>`).
Defensive patterns

Strategy: retry

Try / catch

if _, err := d.SetPolicy(string(policyBytes)); err != nil {
    if isLocked(err) || isBusy(err) {
        // ensure server stopped, then retry once
    }
    return err
}

Prevention

When it happens

Trigger: SQLite database locked by another process; disk full; transaction aborted; database opened read-only. The policy content has already passed validation at this point.

Common situations: headscale was started again between the confirmation prompt and the write; low disk on the volume holding the SQLite file; container with a read-only mount.

Related errors


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