juanfont/headscale · error

test pattern is required as first argument or use --test fla

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.

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)

Solutions

  1. Read the wrapped error: 'connection refused' means the target is not listening — check container status and port mapping.
  2. curl the login URL from the host to verify reachability and TLS.
  3. Ensure the OIDC provider container is healthy before starting the login flow.
  4. 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

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


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/0ad7ed348358f86c. Report an issue: GitHub.