{"record":{"id":"2c976011823bc277","repo":"juanfont/headscale","slug":"listing-users-w-2c9760","errorCode":null,"errorMessage":"listing users: %w","messagePattern":"listing users: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"integration/helpers.go","lineNumber":1077,"sourceCode":"\n// oidcMockUser creates a [mockoidc.MockUser] for OIDC authentication testing.\n// Generates consistent test user data with configurable email verification status\n// for validating OIDC integration flows in headscale authentication tests.\nfunc oidcMockUser(username string, emailVerified bool) mockoidc.MockUser {\n\treturn mockoidc.MockUser{\n\t\tSubject:           username,\n\t\tPreferredUsername: username,\n\t\tEmail:             username + \"@headscale.net\",\n\t\tEmailVerified:     emailVerified,\n\t}\n}\n\n// GetUserByName retrieves a user by name from the headscale server.\n// This is a common pattern used when creating preauth keys or managing users.\nfunc GetUserByName(headscale ControlServer, username string) (*clientv1.User, error) {\n\tusers, err := headscale.ListUsers()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"listing users: %w\", err)\n\t}\n\n\tfor _, u := range users {\n\t\tif u.Name == username {\n\t\t\treturn u, nil\n\t\t}\n\t}\n\n\treturn nil, fmt.Errorf(\"user %s not found\", username) //nolint:err113\n}\n\n// findNode returns the first node in nodes for which match returns true,\n// or nil if no node matches.\nfunc findNode(nodes []*clientv1.Node, match func(*clientv1.Node) bool) *clientv1.Node {\n\tfor _, n := range nodes {\n\t\tif match(n) {\n\t\t\treturn n\n\t\t}","sourceCodeStart":1059,"sourceCodeEnd":1095,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/integration/helpers.go#L1059-L1095","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Check the headscale container is healthy before the failing step (control_logs / hi doctor)","Inspect the wrapped error for gRPC status codes (Unavailable, Unauthenticated) and fix transport/auth accordingly","Re-create the control-server client after server restarts instead of reusing a dead connection"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// health gate before lookups\nfunc controlHealthy(h ControlServer) error {\n    if _, err := h.ListUsers(); err != nil {\n        return fmt.Errorf(\"control plane not ready: %w\", err)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"u, err := GetUserByName(h, name)\nif err != nil {\n    if strings.Contains(err.Error(), \"listing users\") {\n        // transport/API problem: recreate client, check container, then retry once\n    }\n    // 'not found' branch: create the user first\n    return err\n}","preventionTips":["Create users before tests that look them up","Rebuild control clients after server restarts; don't reuse pooled gRPC connections"],"tags":["integration-test","api","grpc","users"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}