juanfont/headscale · error · ErrRegistrationExpired
registration expired
Error message
registration expired
What it means
ErrRegistrationExpired is a sentinel error in hscontrol/state/state.go:114 signalling that a node registration attempt timed out before an auth verdict arrived. The register cache resolves entries that time out or are evicted with this error (state.go:221 rn.FinishAuth(types.AuthVerdict{Err: ErrRegistrationExpired}); auth_cache_test.go confirms evicted entries surface it). API layers map it to a register-failed response (v1/errors.go:27).
Source
Thrown at hscontrol/state/state.go:114
"NodeKey",
"DiscoKey",
"Endpoints",
"Hostinfo",
"IPv4",
"IPv6",
"Hostname",
"GivenName",
"UserID",
"RegisterMethod",
"Tags",
"Expiry",
"LastSeen",
"ApprovedRoutes",
"UpdatedAt",
}
// ErrRegistrationExpired is returned when a registration has expired.
var ErrRegistrationExpired = errors.New("registration expired")
// ErrNodeKeyInUse is returned when a registration or re-auth claims a NodeKey
// already bound to a different machine, enforcing the 1:1 NodeKey<->MachineKey
// binding.
var ErrNodeKeyInUse = errors.New("node key already in use by another machine")
// ErrAmbiguousNodeOwnership is returned when a machine key maps to a set of
// nodes from which the correct one to update or convert cannot be determined:
// multiple user-owned candidates for a tagged conversion, or a tagged node and
// a user-owned node coexisting (impossible per validateNodeOwnership). The
// registration is rejected rather than mutating an arbitrarily-picked node.
var ErrAmbiguousNodeOwnership = errors.New("machine key maps to ambiguous node ownership")
// sshCheckPair identifies a (source, destination) node pair for
// SSH check auth tracking.
type sshCheckPair struct {
Src types.NodeID
Dst types.NodeIDView on GitHub (pinned to 565fd254d0)
Solutions
- Re-run 'tailscale up/login' on the node to start a fresh registration
- Complete the auth flow promptly after the registration URL is issued
- Increase tuning.register_cache_expiration if auth legitimately takes longer (e.g. slow MFA)
- Increase tuning.register_cache_max_entries under registration bursts
Example fix
null
Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
null
Try / catch
if errors.Is(err, state.ErrRegistrationExpired) {
// safe to retry: run tailscale up/login again for a fresh registration URL
} Prevention
- Complete registration auth promptly after the URL is printed
- Size tuning.register_cache_expiration / register_cache_max_entries to your MFA latency and registration volume
When it happens
Trigger: A node starts registration (interactive URL or pre-auth key flow) but no verdict arrives within tuning.register_cache_expiration; the cache entry is evicted (max entries exceeded) before the user completes auth; the OIDC callback or key validation never lands.
Common situations: User opens the registration URL but completes login after the timeout; heavy registration bursts exceeding register_cache_max_entries (default 1024) evicting pending entries; slow IdP or unreachable callback during OIDC registration.
Related errors
- registration info not in cache
- auth ID has invalid length
- auth ID has invalid prefix
- auth-key expired
- auth-key has already been used
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/838bcbb1b2dd96f8.
Report an issue: GitHub.