juanfont/headscale · error

system checks failed

Error message

system checks failed

What it means

Guard error from doLoginURLWithClient when loginURL is nil. The function immediately dereferences loginURL.String(), so a nil URL is rejected up front with the hostname for context.

Source

Thrown at cmd/hi/doctor.go:29

	"github.com/juanfont/headscale/integration/dockertestutil"
	"github.com/juanfont/headscale/integration/k3sic"
)

const (
	statusPass = "PASS"
	statusFail = "FAIL"
	statusWarn = "WARN"

	nameDockerDaemon  = "Docker Daemon"
	nameDockerContext = "Docker Context"
	nameDockerSocket  = "Docker Socket"
	nameGolangImage   = "Golang Image"
	nameK3sImage      = "K3s Image"
	nameGoInstall     = "Go Installation"
)

var ErrSystemChecksFailed = errors.New("system checks failed")

// DoctorResult represents the result of a single health check.
type DoctorResult struct {
	Name        string
	Status      string // "PASS", "FAIL", "WARN"
	Message     string
	Suggestions []string
}

// pass builds a passing DoctorResult.
func pass(name, message string) DoctorResult {
	return DoctorResult{Name: name, Status: statusPass, Message: message}
}

// warn builds a warning DoctorResult with optional suggestions.
func warn(name, message string, suggestions ...string) DoctorResult {
	return DoctorResult{Name: name, Status: statusWarn, Message: message, Suggestions: suggestions}
}

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Check where the loginURL was obtained; verify that step's error was handled.
  2. Confirm the node is in NeedsLogin state before extracting a login URL.

Example fix

// before
loginURL, _ := client.GetLoginURL()
body, redir, err := doLoginURLWithClient(hostname, loginURL, hc, true)
// after
loginURL, err := client.GetLoginURL()
if err != nil || loginURL == nil {
    return "", nil, fmt.Errorf("getting login URL: %w", err)
}
body, redir, err := doLoginURLWithClient(hostname, loginURL, hc, true)
Defensive patterns

Strategy: type-guard

Validate before calling

if loginURL == nil || loginURL.String() == "" {
    return fmt.Errorf("login URL missing; is the node in NeedsLogin state?")
}

Type guard

func hasLoginURL(u *url.URL) bool {
    return u != nil && u.IsAbs() && u.Host != ""
}

Prevention

When it happens

Trigger: Passing a nil *url.URL — typically because a prior step that extracts the login URL from tailscaled output failed and returned nil, and the caller ignored that error.

Common situations: Login URL parsing/extraction failed earlier (tailscale not in NeedsLogin state, `tailscale login` output format changed), error ignored, nil propagated into this function.

Related errors


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