juanfont/headscale · warning
resolving: %w
Error message
resolving: %w
What it means
The Src alias in a PolicyTest parsed successfully but Alias.Resolve failed while turning it into IP prefixes. This wraps resolution errors such as a user not found (ErrUserNotFound), an unknown host (ErrHostResolve), or an unsupported autogroup — the alias is well-formed but does not name anything resolvable in the current policy and user database.
Source
Thrown at hscontrol/policy/v2/test.go:330
check(test.Accept, true, &res.AcceptOK, &res.AcceptFail)
check(test.Deny, false, &res.DenyOK, &res.DenyFail)
return res
}
// resolveTestSource resolves the Src alias of a [PolicyTest] into a slice of
// [netip.Prefix]. [parseAlias] + [Alias.Resolve] cover every alias type the rest
// of the policy engine supports, so tests inherit alias semantics for free.
func resolveTestSource(src string, pol *Policy, users []types.User, nodes views.Slice[types.NodeView]) ([]netip.Prefix, error) {
alias, err := parseAlias(src)
if err != nil {
return nil, fmt.Errorf("invalid alias: %w", err)
}
addrs, err := alias.Resolve(pol, users, nodes)
if err != nil {
return nil, fmt.Errorf("resolving: %w", err)
}
if addrs == nil || addrs.Empty() {
return nil, nil
}
return addrs.Prefixes(), nil
}
// evalReachability reports whether traffic from any srcPrefix to dst (in
// `host:port` form) is allowed by filter for the requested protocol.
//
// Empty proto means the default set the client applies when proto is
// omitted (TCP/UDP/ICMP) — we accept a rule whose IPProto list contains
// any of those, or rules with no IPProto restriction at all.
func evalReachability(srcPrefixes []netip.Prefix, dst string, proto Protocol, pol *Policy, filter []tailcfg.FilterRule, users []types.User, nodes views.Slice[types.NodeView]) (bool, error) {
awp, err := parseDestinationAlias(dst)
if err != nil {View on GitHub (pinned to 565fd254d0)
Solutions
- Identify the wrapped error — it is printed after 'resolving:' and is usually ErrUserNotFound, ErrMultipleUsersFound, ErrHostResolve, or ErrUnknownAutogroup; fix the specific cause.
- If Src is a user, confirm the user exists (headscale users list) and that email or username matches exactly.
- If Src is a host, confirm it is declared in the 'hosts' section of the same policy file.
- Replace unsupported autogroups (e.g. autogroup:nonroot) with an explicit group or tag.
Example fix
// before
"tests": [{"src": "autogroup:nonroot", "accept": ["api:443"]}]
// after
"tests": [{"src": "group:teamdev", "accept": ["api:443"]}] Defensive patterns
Strategy: validation
Validate before calling
// Before running tests, verify each user alias exists in the user set.
func srcUserExists(src string, users types.Users) bool {
if !strings.Contains(src, "@") { return true } // not a user alias
for _, u := range users {
if u.Email == src || u.Name == src { return true }
}
return false
} Try / catch
if err := runPolicyTests(...); err != nil {
if errors.Is(err, v2.ErrUserNotFound) || errors.Is(err, v2.ErrHostResolve) || errors.Is(err, v2.ErrUnknownAutogroup) {
// resolution-time problem: fix fixture data, not the policy grammar
}
return err
} Prevention
- Seed test fixtures (users, nodes, hosts) before running policy tests.
- Reference groups instead of individual users so renames do not break resolution.
- Treat any 'resolving:' wrap as a data-sync issue; read the inner error first.
When it happens
Trigger: A test Src like 'ghost@example.com' where no user with that email/name exists, 'autogroup:nonroot' (unsupported), or a hostname that is not in the policy's hosts map. alias.Resolve(pol, users, nodes) returns an error after parseAlias succeeded.
Common situations: Policy tests reference users that exist in the ACL grants but not in the headscale user database used for the test run; renaming users in the DB but not in tests; using Tailscale-documented autogroups that headscale has not implemented.
Related errors
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/c9bcd14ae919bbfc.
Report an issue: GitHub.