juanfont/headscale · error
listing updated clients: %w
Error message
listing updated clients: %w
What it means
Thrown inside a backoff.Retry loop in Scenario.AddAndLoginClient when s.ListTailscaleClients(username) returns an error while polling for a newly created tailscale node to appear. The list call executes `headscalectl listnodes` (or equivalent) inside the headscale container, so any container exec failure, CLI error, or malformed output surfaces here. The retry wrapper keeps invoking it for up to 10s, so this error only propagates if listing fails every attempt.
Source
Thrown at integration/helpers.go:1169
// Get the original client list
originalClients, err := s.ListTailscaleClients(username)
if err != nil {
return nil, fmt.Errorf("listing original clients: %w", err)
}
// Create the new node
err = s.CreateTailscaleNodesInUser(username, version, 1, tsOpts...)
if err != nil {
return nil, fmt.Errorf("creating tailscale node: %w", err)
}
// Wait for the new node to appear in the client list
var newClient TailscaleClient
_, err = backoff.Retry(t.Context(), func() (struct{}, error) {
updatedClients, err := s.ListTailscaleClients(username)
if err != nil {
return struct{}{}, fmt.Errorf("listing updated clients: %w", err)
}
if len(updatedClients) != len(originalClients)+1 {
return struct{}{}, fmt.Errorf("expected %d clients, got %d", len(originalClients)+1, len(updatedClients)) //nolint:err113
}
newClient, err = FindNewClient(originalClients, updatedClients)
if err != nil {
return struct{}{}, fmt.Errorf("finding new client: %w", err)
}
return struct{}{}, nil
}, backoff.WithBackOff(backoff.NewConstantBackOff(500*time.Millisecond)), backoff.WithMaxElapsedTime(10*time.Second))
if err != nil {
return nil, fmt.Errorf("timeout waiting for new client: %w", err)
}
// Get the user and create preauth keyView on GitHub (pinned to 565fd254d0)
Solutions
- Check hs-*.stderr.log in control_logs/<runID>/ to see why the headscale CLI failed inside the container
- Verify the headscale container is still running (docker ps) and did not OOM or panic during the test
- Re-run with `go run ./cmd/hi doctor` to validate the Docker environment before the test
- If the container starts slowly, ensure earlier wait-for-healthy logic completed before AddAndLoginClient is called
Example fix
// No code fix; the wrapping backoff.Retry already retries for 10s. // If listing fails the whole time, inspect container logs: // docker logs <headscale-container> --tail 100
Defensive patterns
Strategy: retry
Validate before calling
// Pre-condition: headscale container healthy before adding clients
if err := headscale.WaitForRunning(30 * time.Second); err != nil {
t.Fatalf("headscale not ready: %v", err)
} Try / catch
// The backoff.Retry already retries; treat final error as fatal for the test:
client, err := scenario.AddAndLoginClient(t, user, ver)
if err != nil {
t.Fatalf("AddAndLoginClient failed (check hs-*.stderr.log): %v", err)
} Prevention
- Always wait for the headscale container to be healthy before creating nodes
- Keep control_logs run IDs and read hs-*.stderr.log on any listing failure
- Run `go run ./cmd/hi doctor` before integration suites
When it happens
Trigger: Calling Scenario.AddAndLoginClient after CreateTailscaleNodesInUser; each 500ms retry invokes Scenario.ListTailscaleClients(username), which shells into the headscale container and parses node-list JSON. Fails when the headscale container is not running, the exec times out, the CLI exits non-zero, or node JSON cannot be decoded.
Common situations: Head scale container crashed mid-test; Docker daemon under load causing exec timeouts; headscale not yet listening when the first list attempts run; a broken/unbuilt headscale binary inside the container returning errors on every invocation.
Related errors
- pulling image %s: %w
- creating certificates for derp test: %w
- %s starting tailscale DERPer container (version: %s): %w
- writing TLS certificate to container: %w
- writing TLS key to container: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/e8047057f7906782.
Report an issue: GitHub.