{"record":{"id":"72c46a9db8791e8d","repo":"juanfont/headscale","slug":"getting-ips-w","errorCode":null,"errorMessage":"getting IPs: %w","messagePattern":"getting IPs: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"integration/scenario.go","lineNumber":1408,"sourceCode":"---\n%s - method: %s | url: %s\n%s - status: %d | cookies: %+v\n---\n`, t.Hostname, req.Method, req.URL.String(), t.Hostname, resp.StatusCode, resp.Cookies())\n\n\treturn resp, nil\n}\n\n// GetIPs returns all [netip.Addr] of [TailscaleClient]s associated with a [User]\n// in a [Scenario].\nfunc (s *Scenario) GetIPs(user string) ([]netip.Addr, error) {\n\tvar ips []netip.Addr\n\n\tif ns, ok := s.users[user]; ok {\n\t\tfor _, client := range ns.Clients {\n\t\t\tclientIps, err := client.IPs()\n\t\t\tif err != nil {\n\t\t\t\treturn ips, fmt.Errorf(\"getting IPs: %w\", err)\n\t\t\t}\n\n\t\t\tips = append(ips, clientIps...)\n\t\t}\n\n\t\treturn ips, nil\n\t}\n\n\treturn ips, fmt.Errorf(\"getting IPs: %w\", errNoUserAvailable)\n}\n\n// GetClients returns all [TailscaleClient]s associated with a [User] in a [Scenario].\nfunc (s *Scenario) GetClients(user string) ([]TailscaleClient, error) {\n\tif ns, ok := s.users[user]; ok {\n\t\treturn xmaps.Values(ns.Clients), nil\n\t}\n\n\treturn nil, fmt.Errorf(\"getting clients: %w\", errNoUserAvailable)","sourceCodeStart":1390,"sourceCodeEnd":1426,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/integration/scenario.go#L1390-L1426","documentation":"Inside `Scenario.GetIPs(user)`, one of the user's tailscale clients failed to report its IP addresses (`client.IPs()` returned an error). The partial `ips` slice is returned alongside the error. Typically the underlying failure is a `tailscale status`/`ip` command failing inside the client container.","triggerScenarios":"Iterating `ns.Clients` and calling `client.IPs()` while a container is still starting, has crashed, or its tailscaled is not yet running — the wrapped error describes the exact command failure.","commonSituations":"Calling GetIPs immediately after client creation without waiting for tailscaled readiness; container exited during the test; retries needed because IP assignment lags login.","solutions":["Read the wrapped error — it names the client and the command that failed.","Wait for the client to be ready (login done, tailscaled up) before querying IPs — use an Eventually-style retry in tests.","Check `docker ps` / container logs for the failing client."],"exampleFix":"// before\nips, err := scenario.GetIPs(user) // direct call, fails while tailscaled boots\n\n// after\nrequire.Eventually(t, func() bool {\n    _, err := scenario.GetIPs(user)\n    return err == nil\n}, 30*time.Second, 500*time.Millisecond)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Tolerate startup lag with bounded retries.\nvar ips []netip.Addr\nerr := retry(10, 2*time.Second, func() error {\n    var e error\n    ips, e = s.GetIPs(user)\n    return e\n})","preventionTips":["Wait for client login/state before querying IPs (EventuallyWithT).","Check container liveness when a single client repeatedly fails.","Remember GetIPs returns partial data with the error — do not use ips on err != nil."],"tags":["integration","tailscale","readiness"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}