juanfont/headscale · error

deleting ephemeral node: %w

Error message

deleting ephemeral node: %w

What it means

Wraps a failure of h.state.DeleteNode while removing an ephemeral node during logout (hscontrol/auth.go:220). Ephemeral nodes (registered with an ephemeral pre-auth key) are deleted from the state layer and database on logout; any DB or consistency error — foreign key constraints from related records, lock timeouts, connection failures — is surfaced with this message and the logout response is not returned.

Source

Thrown at hscontrol/auth.go:220

	// If the request expiry is in the past, we consider it a logout.
	// Zero expiry is handled in [Headscale.handleRegister] before calling this function.
	if req.Expiry.Before(time.Now()) {
		log.Debug().
			EmbedObject(node).
			Bool("is_ephemeral", node.IsEphemeral()).
			Bool("has_authkey", node.AuthKey().Valid()).
			Time("req.expiry", req.Expiry).
			Msg("Processing logout request with past expiry")

		if node.IsEphemeral() {
			log.Info().
				EmbedObject(node).
				Msg("Deleting ephemeral node during logout")

			c, err := h.state.DeleteNode(node)
			if err != nil {
				return nil, fmt.Errorf("deleting ephemeral node: %w", err)
			}

			h.Change(c)

			return &tailcfg.RegisterResponse{
				NodeKeyExpired:    true,
				MachineAuthorized: false,
			}, nil
		}

		log.Debug().
			EmbedObject(node).
			Msg("Node is not ephemeral, setting expiry instead of deleting")
	}

	// Tagged nodes have key expiry permanently disabled (they are owned by
	// their tags, not a user, and never expire - KB 1068). Logging one out has
	// no expiry semantics, so do not stamp an expiry on it: doing so leaves the

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Check the wrapped DB error in logs; constraint violations indicate related rows not cascading — upgrade headscale (deletion order fixes land periodically).
  2. For lock contention, reduce simultaneous logouts (stagger termination) or switch SQLite deployments to PostgreSQL.
  3. Retry: the node can re-attempt logout; if the node is gone anyway, an admin can remove it manually via `headscale nodes delete -i <id>`.
  4. If persistent, dump logs plus schema version and report upstream.
Defensive patterns

Strategy: retry

Try / catch

resp, err := h.handleRegister(req, mk)
if err != nil && strings.Contains(err.Error(), "deleting ephemeral node") {
    // teardown failed; node stays registered — retry logout or delete via `headscale nodes delete`
}

Prevention

When it happens

Trigger: An ephemeral node (ephemeral=true authkey) logs out while the DB is under write load; deletion trips FK constraints on related rows (routes, pre-auth-key usages); SQLite 'database is locked' during concurrent mapper updates; PostgreSQL unreachable mid-transaction.

Common situations: CI/ephemeral CI runners deregistering en masse at job end; autoscaling groups terminating ephemeral nodes simultaneously; DB maintenance overlapping with node churn.

Related errors


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