juanfont/headscale · error

listing users: %w

Error message

listing users: %w

What it means

Returned by the integration helper GetUserByName when headscale.ListUsers (the gRPC/API call to the control server) fails. It wraps the transport/API error; the user-lookup itself hasn't started yet.

Source

Thrown at integration/helpers.go:1077

// oidcMockUser creates a [mockoidc.MockUser] for OIDC authentication testing.
// Generates consistent test user data with configurable email verification status
// for validating OIDC integration flows in headscale authentication tests.
func oidcMockUser(username string, emailVerified bool) mockoidc.MockUser {
	return mockoidc.MockUser{
		Subject:           username,
		PreferredUsername: username,
		Email:             username + "@headscale.net",
		EmailVerified:     emailVerified,
	}
}

// GetUserByName retrieves a user by name from the headscale server.
// This is a common pattern used when creating preauth keys or managing users.
func GetUserByName(headscale ControlServer, username string) (*clientv1.User, error) {
	users, err := headscale.ListUsers()
	if err != nil {
		return nil, fmt.Errorf("listing users: %w", err)
	}

	for _, u := range users {
		if u.Name == username {
			return u, nil
		}
	}

	return nil, fmt.Errorf("user %s not found", username) //nolint:err113
}

// findNode returns the first node in nodes for which match returns true,
// or nil if no node matches.
func findNode(nodes []*clientv1.Node, match func(*clientv1.Node) bool) *clientv1.Node {
	for _, n := range nodes {
		if match(n) {
			return n
		}

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Check the headscale container is healthy before the failing step (control_logs / hi doctor)
  2. Inspect the wrapped error for gRPC status codes (Unavailable, Unauthenticated) and fix transport/auth accordingly
  3. Re-create the control-server client after server restarts instead of reusing a dead connection
Defensive patterns

Strategy: try-catch

Validate before calling

// health gate before lookups
func controlHealthy(h ControlServer) error {
    if _, err := h.ListUsers(); err != nil {
        return fmt.Errorf("control plane not ready: %w", err)
    }
    return nil
}

Try / catch

u, err := GetUserByName(h, name)
if err != nil {
    if strings.Contains(err.Error(), "listing users") {
        // transport/API problem: recreate client, check container, then retry once
    }
    // 'not found' branch: create the user first
    return err
}

Prevention

When it happens

Trigger: Calling GetUserByName in a test when the control-server client is misconfigured, the headscale container is down/restarting, or the API returns an error (auth, unavailable).

Common situations: Test harness points at a stale server URL; headscale container crashed earlier in the test; API key expired mid-run; grpc connection pool reused after server restart.

Related errors


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