juanfont/headscale · error
test pattern is required as first argument or use --test…
Error message
test pattern is required as first argument or use --test flag
What it means
Wrapped error from doLoginURLWithClient when hc.Do(req) fails: the login GET request could not be completed. Covers connection refused, DNS failure, TLS errors, redirects failing, and context cancellation. The hostname prefix says which node's login flow broke.
Solutions
- Read the wrapped error: 'connection refused' means the target is not listening — check container status and port mapping.
- curl the login URL from the host to verify reachability and TLS.
- Ensure the OIDC provider container is healthy before starting the login flow.
- Re-run; transient Docker networking resets are common in CI.
Defensive patterns
Strategy: retry
Validate before calling
// cheap reachability check before the login GET
conn, err := net.DialTimeout("tcp", loginURL.Host, 2*time.Second)
if err != nil {
return fmt.Errorf("login target unreachable: %w", err)
}
conn.Close() Try / catch
resp, redir, err := doLoginURLWithClient(hostname, loginURL, hc, true)
if err != nil {
if strings.Contains(err.Error(), "sending http request") {
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
// retry once after a short backoff
}
}
} Prevention
- Ensure headscale and the OIDC provider containers are healthy before login flows.
- Use host-resolvable server_url values in test configs.
- Retain full wrapped errors (never replace %w chains) so transport causes stay visible.
When it happens
Trigger: OIDC login against a login URL whose host is unreachable from the test process — headscale/OIDC provider container not listening, wrong hostname resolution, TLS certificate mismatch, or connection reset mid-request.
Common situations: Headscale container restarted between URL issuance and the GET, Docker network DNS flakiness, OIDC provider (e.g. dex) not yet ready, or the control server URL using a hostname only resolvable inside the Docker network.
Related errors
- not confirmed, aborting
- tag must start with the string 'tag:'
- auth request rejected
- directory is required
- reading confirm response
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/0ad7ed348358f86c.
Report an issue: GitHub.
Appendix: source
Thrown at cmd/hi/run.go:15
package main
import (
"errors"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"time"
"github.com/creachadair/command"
)
var ErrTestPatternRequired = errors.New("test pattern is required as first argument or use --test flag")
type RunConfig struct {
TestPattern string `flag:"test,Test pattern to run"`
Timeout time.Duration `flag:"timeout,default=120m,Test timeout"`
FailFast bool `flag:"failfast,default=true,Stop on first test failure"`
UsePostgres bool `flag:"postgres,default=false,Use PostgreSQL instead of SQLite"`
GoVersion string `flag:"go-version,Go version to use (auto-detected from go.mod)"`
CleanBefore bool `flag:"clean-before,default=true,Clean stale resources before test"`
CleanAfter bool `flag:"clean-after,default=true,Clean resources after test"`
KeepOnFailure bool `flag:"keep-on-failure,default=false,Keep containers on test failure"`
LogsDir string `flag:"logs-dir,default=control_logs,Control logs directory"`
Verbose bool `flag:"verbose,default=false,Verbose output"`
Stats bool `flag:"stats,default=false,Collect and display container resource usage statistics"`
HSMemoryLimit float64 `flag:"hs-memory-limit,default=0,Fail test if any Headscale container exceeds this memory limit in MB (0 = disabled)"`
TSMemoryLimit float64 `flag:"ts-memory-limit,default=0,Fail test if any Tailscale container exceeds this memory limit in MB (0 = disabled)"`
}
// runIntegrationTest executes the integration test workflow.View on GitHub (pinned to 565fd254d0)