juanfont/headscale · error
loading ACL policy: %w
Error message
loading ACL policy: %w
What it means
Thrown in `headscale policy get` (server mode) when client.GetPolicyWithResponse() fails at the transport level — the HTTP request to the headscale API never completed. This is distinct from the apiError branch which handles non-200 responses.
Source
Thrown at cmd/headscale/cli/policy.go:96
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
})
if err != nil {
return err
}
}
// This does not pass output format as we don't support yaml, json or
// json-line output for this command. It is HuJSON already.
fmt.Println(policyData)View on GitHub (pinned to 565fd254d0)
Solutions
- Check the server is up: `curl -v <server_url>/api/v1/policy`.
- Verify the configured server address and API key in the CLI environment.
- If TLS is the issue, ensure the certificate is trusted by the CLI host or fix server_url to match the cert.
- If the server is intentionally down, use the --bypass-server-and-access-database-directly flag instead.
Defensive patterns
Strategy: retry
Validate before calling
// pre-flight: server API reachable?
func pingAPI(serverURL string, apiKey string) error {
req, _ := http.NewRequest("GET", serverURL+"/api/v1/apikey", nil)
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
resp.Body.Close()
if resp.StatusCode >= 500 {
return fmt.Errorf("server unhealthy: %d", resp.StatusCode)
}
return nil
} Type guard
func isTransportError(err error) bool {
return err != nil && !strings.Contains(err.Error(), "api error (")
} Try / catch
err := withClient(func(ctx, c) error { ... GetPolicyWithResponse ... })
if err != nil && isTransportError(err) {
// network-level: backoff and retry once; if still failing, fall back to --bypass only if server is confirmed down
} Prevention
- Health-check the server before scripted policy operations.
- Prefer server mode for reads; reserve bypass for maintenance windows.
- Pin server_url in one place (env/config) so all CLI hosts agree.
When it happens
Trigger: headscale server not running or unreachable at the configured address; wrong port; TLS certificate mismatch; DNS failure; connection refused/reset. Note: an HTTP 4xx/5xx does NOT produce this error — that goes through apiError.
Common situations: CLI run on a machine without access to the server; server_url pointing at stale address after moving the deployment; self-signed cert without the CLI trusting it; firewall dropping the connection.
Related errors
- listing preauthkeys: %w
- creating preauthkey: %w
- expiring preauthkey: %w
- deleting preauthkey: %w
- health check timed out
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/42d46c175a41a027.
Report an issue: GitHub.