juanfont/headscale · warning
executing command, expected string %q not found in %q
Error message
executing command, expected string %q not found in %q
What it means
Produced by assertCommandOutputContains when the command executes successfully but its stdout does not contain the expected substring. It is retried with exponential backoff for up to 10 seconds (eventual consistency for DNS/route/policy propagation), then surfaces as a test failure via assert.NoError.
Source
Thrown at integration/helpers.go:912
// assertCommandOutputContains executes a command with exponential backoff retry until the output
// contains the expected string or timeout is reached (10 seconds).
// This implements eventual consistency patterns and should be used instead of [time.Sleep]
// before executing commands that depend on network state propagation.
//
// Timeout: 10 seconds with exponential backoff
// Use cases: DNS resolution, route propagation, policy updates.
func assertCommandOutputContains(t *testing.T, c TailscaleClient, command []string, contains string) {
t.Helper()
_, err := backoff.Retry(t.Context(), func() (struct{}, error) {
stdout, stderr, err := c.Execute(command)
if err != nil {
return struct{}{}, fmt.Errorf("executing command, stdout: %q stderr: %q, err: %w", stdout, stderr, err)
}
if !strings.Contains(stdout, contains) {
return struct{}{}, fmt.Errorf("executing command, expected string %q not found in %q", contains, stdout) //nolint:err113
}
return struct{}{}, nil
}, backoff.WithBackOff(backoff.NewExponentialBackOff()), backoff.WithMaxElapsedTime(10*time.Second))
assert.NoError(t, err)
}
// dockertestMaxWait returns the maximum wait time for Docker-based test operations.
// Uses longer timeouts in CI environments to account for slower resource allocation
// and higher system load during automated testing.
func dockertestMaxWait() time.Duration {
wait := 300 * time.Second //nolint
if util.IsCI() {
wait = 600 * time.Second //nolint
}
View on GitHub (pinned to 565fd254d0)
Solutions
- Compare the actual stdout shown in the failure against the expected string for typos/formatting differences
- Verify the precondition (route enabled, policy applied, peer registered) actually holds before asserting
- If propagation is legitimately slow, split the wait from the assertion or assert on a more stable marker
Defensive patterns
Strategy: retry
Validate before calling
// cheap pre-check: run the command and inspect output yourself when debugging
stdout, _, err := c.Execute(command)
if err == nil && strings.Contains(stdout, contains) {
// safe to assert; otherwise let the helper retry
} Prevention
- Copy expected strings from actual output once, then pin them as constants
- Assert on stable identifiers (node IDs, IPs) rather than display names that reformat
When it happens
Trigger: Calling assertCommandOutputContains(t, client, []string{"tailscale", "status"}, "peer-name") when the expected string hasn't propagated yet or is genuinely absent — wrong peer name, route not enabled, policy blocking visibility.
Common situations: Assertions made with a wrong expected string (typo in node name); state that takes longer than the 10 s budget to converge on slow CI; tests that assume immediate consistency after policy changes.
Related errors
- executing command, stdout: %q stderr: %q, err: %w
- dumping config: %w
- creating certificates for derp test: %w
- %s starting tailscale DERPer container (version: %s): %w
- writing TLS certificate to container: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/555202814e3202d4.
Report an issue: GitHub.