juanfont/headscale · error
expected %d clients, got %d
Error message
expected %d clients, got %d
What it means
Emitted from the retry lambda in Scenario.AddAndLoginClient when the number of clients returned by ListTailscaleClients does not equal the pre-creation count plus one. It is an assertion-style error (nolint:err113) used as a retry signal: the newly created container node has not registered with headscale yet, so the count check fails and backoff.Retry re-invokes the lambda after 500ms.
Source
Thrown at integration/helpers.go:1173
}
// 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 key
user, err := GetUserByName(headscale, username)
if err != nil {
return nil, fmt.Errorf("getting user: %w", err)
}View on GitHub (pinned to 565fd254d0)
Solutions
- Read the new tailscale container's logs (docker logs) to see if tailscaled failed to register or retried in a loop
- Check hs-*.stderr.log for registration errors such as TLS or noise handshake failures
- Free Docker resources or raise backoff.WithMaxElapsedTime if the host is consistently slow
- Verify the tailscale image version is compatible with the headscale version under test
Defensive patterns
Strategy: retry
Prevention
- Treat this error as a timing signal, not a bug: it only surfaces after 10s of registration not completing
- Keep Docker host load low; avoid running many suites in parallel on one daemon
- Pin tailscale image versions known to work with the headscale version under test
When it happens
Trigger: Scenario.CreateTailscaleNodesInUser started a new tailscale container, but the node has not completed registration with the control server yet (or never will because its container is broken). Every retry sees len(updatedClients) != len(originalClients)+1 for 10 seconds, then the error propagates wrapped as 'timeout waiting for new client'.
Common situations: Slow or overloaded Docker host so tailscaled takes >10s to register; wrong tailscale version image that cannot talk to the headscale API; TLS mismatch so the node registration silently fails; headscale server rejecting registrations due to policy config.
Related errors
- listing updated clients: %w
- timeout waiting for new client: %w
- failed to allocate IP
- dumping config: %w
- pulling image %s: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/8b8a251f897adf8e.
Report an issue: GitHub.