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

  1. Identify the wrapped error — it is printed after 'resolving:' and is usually ErrUserNotFound, ErrMultipleUsersFound, ErrHostResolve, or ErrUnknownAutogroup; fix the specific cause.
  2. If Src is a user, confirm the user exists (headscale users list) and that email or username matches exactly.
  3. If Src is a host, confirm it is declared in the 'hosts' section of the same policy file.
  4. 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

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.