juanfont/headscale · error · ErrNodeKeyInUse

node key already in use by another machine

Error message

node key already in use by another machine

What it means

ErrNodeKeyInUse is a sentinel error in hscontrol/state/state.go:119 enforcing the invariant that a NodeKey binds 1:1 to one machine. It is returned at state.go:1744, 1911 and 2602 when a registration or re-auth presents a NodeKey already bound to a different machine (different MachineKey). Covered by auth_nodekey_binding_test.go and persist_test.go; API layers translate it to a machine-key-conflict response (v1/errors.go:42).

Source

Thrown at hscontrol/state/state.go:119

	"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.NodeID
}

// State manages Headscale's core state, coordinating between database, policy management,
// IP allocation, and DERP routing. All methods are thread-safe.
//

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Run 'tailscale logout' or delete the local state (/var/lib/tailscale) on the duplicate machine so it generates a fresh node key
  2. Alternatively delete the existing node (headscale node delete -i <id>) that holds the key, then re-register
  3. Never clone the tailscale state directory when imaging machines
  4. If the machine key intentionally changed (hardware swap), delete and re-register the node

Example fix

# before: cloned VM reuses node key, registration fails
# after: on the clone, reset identity
tailscale logout
rm -rf /var/lib/tailscale/tailscaled.state
systemctl restart tailscaled
Defensive patterns

Strategy: validation

Validate before calling

null

Type guard

null

Try / catch

if errors.Is(err, state.ErrNodeKeyInUse) {
    // do not retry: delete the conflicting node or reset the client's tailscale state, then re-register
}

Prevention

When it happens

Trigger: Registering a new machine with a NodeKey that an existing node already uses; re-authenticating after a machine-key change while keeping the old node key; restoring a node key backup onto different hardware with a new machine key.

Common situations: Cloning a VM/container including the tailscale state directory so both copies present the same node key; deliberately reusing a node key on new hardware; corrupted/mismatched state after disk imaging.

Related errors


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