juanfont/headscale · error

auto approving routes: %w

Error message

auto approving routes: %w

What it means

Wraps a failure of h.state.AutoApproveRoutes(node) at the end of registration with an auth key (hscontrol/auth.go:453). After the node is persisted and added to the policy manager, routes the node advertises and the policy allows are auto-approved; this error means that approval step (route matching plus a subsequent state save of the node) failed. Note the comment in the source: the node add and the route approval are two separate saves, so a failure here can leave the node registered but its routes unapproved.

Source

Thrown at hscontrol/auth.go:453

		return nil, nil //nolint:nilnil // intentional: no node to return when ephemeral deleted
	}

	// This is a bit of a back and forth, but we have a bit of a chicken and egg
	// dependency here.
	// Because the way the policy manager works, we need to have the node
	// in the database, then add it to the policy manager and then we can
	// approve the route. This means we get this dance where the node is
	// first added to the database, then we add it to the policy manager via
	// nodesChangedHook and then we can auto approve the routes.
	// As that only approves the struct object, we need to save it again and
	// ensure we send an update.
	// This works, but might be another good candidate for doing some sort of
	// eventbus.
	// TODO(kradalby): This needs to be ran as part of the batcher maybe?
	// now since we dont update the node/pol here anymore
	routesChange, err := h.state.AutoApproveRoutes(node)
	if err != nil {
		return nil, fmt.Errorf("auto approving routes: %w", err)
	}

	// Send both changes. Empty changes are ignored by Change().
	h.Change(changed, routesChange)

	resp := &tailcfg.RegisterResponse{
		MachineAuthorized: true,
		NodeKeyExpired:    node.IsExpired(),
		User:              node.Owner().TailscaleUser(),
		Login:             node.Owner().TailscaleLogin(),
	}

	log.Trace().
		Caller().
		Interface("reg.resp", resp).
		Interface("reg.req", req).
		EmbedObject(node).
		Msg("RegisterResponse")

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Check whether the node itself registered (it likely did) and only route approval failed: `headscale nodes list` and `headscale routes list`.
  2. Approve the routes manually: `headscale routes enable -r <route-id>` (or the corresponding route approve command).
  3. Fix the underlying DB issue reported in the wrapped error (locks/connectivity) so future registrations auto-approve.
  4. Retry registration if the node state is inconsistent; re-running with the same auth key policy re-attempts approval.
Defensive patterns

Strategy: fallback

Try / catch

resp, err := h.handleRegister(req, mk)
if err != nil && strings.Contains(err.Error(), "auto approving routes") {
    // node registration itself likely succeeded: check `headscale routes list` and approve manually
}

Prevention

When it happens

Trigger: Registering a node that advertises subnet routes (--advertise-routes) with an autoApprovers policy entry, while the second state save fails: DB lock, connection drop, or policy manager inconsistency after the node was added.

Common situations: Subnet routers registering under load on SQLite; policy file changed concurrently with registration; DB transient failures during bulk router enrollment.

Related errors


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