juanfont/headscale · warning
route is not available on node
Error message
route is not available on node
What it means
Sentinel in hscontrol/db/node.go returned by EnableRoute/DisableRoute-style helpers when asked to toggle a route that the node does not currently advertise. Routes must appear in the node's Hostinfo advertised routes before they can be enabled or disabled as approved subnet routes.
Source
Thrown at hscontrol/db/node.go:45
// defaultTestNodePrefix is the default hostname prefix for nodes created in tests.
defaultTestNodePrefix = "testnode"
)
// ErrNodeNameNotUnique is returned when a node name is not unique.
var ErrNodeNameNotUnique = errors.New("node name is not unique")
// preloadNode returns a session that eager-loads a node's AuthKey, the
// AuthKey's User, and the node's User.
func preloadNode(tx *gorm.DB) *gorm.DB {
return tx.
Preload("AuthKey").
Preload("AuthKey.User").
Preload("User")
}
var (
ErrNodeNotFound = errors.New("node not found")
ErrNodeRouteIsNotAvailable = errors.New("route is not available on node")
ErrNodeNotFoundRegistrationCache = errors.New(
"node not found in registration cache",
)
ErrCouldNotConvertNodeInterface = errors.New("failed to convert node interface")
)
// ListPeers returns peers of node, regardless of any Policy or if the node is expired.
// If no peer IDs are given, all peers are returned.
// If at least one peer ID is given, only these peer nodes will be returned.
func (hsdb *HSDatabase) ListPeers(nodeID types.NodeID, peerIDs ...types.NodeID) (types.Nodes, error) {
return ListPeers(hsdb.DB, nodeID, peerIDs...)
}
// ListPeers returns peers of node, regardless of any Policy or if the node is expired.
// If no peer IDs are given, all peers are returned.
// If at least one peer ID is given, only these peer nodes will be returned.
func ListPeers(tx *gorm.DB, nodeID types.NodeID, peerIDs ...types.NodeID) (types.Nodes, error) {
nodes := types.Nodes{}View on GitHub (pinned to 565fd254d0)
Solutions
- Run `headscale routes list` to see routes actually advertised, and enable by the exact prefix/ID shown
- On the node, re-run `tailscale set --advertise-routes=<prefix>` so the route is advertised, wait for it to appear, then enable
- Fix automation scripts to look up route IDs dynamically instead of hardcoding them
Example fix
# before (route not advertised by node) headscale routes enable --route 5 # after # on the node: tailscale set --advertise-routes=10.42.0.0/24 # on the server: headscale routes list headscale routes enable --route <id-from-list>
Defensive patterns
Strategy: validation
Validate before calling
// confirm the node advertises the prefix before enabling it
node, _ := hsdb.GetNodeByID(nodeID)
for _, r := range node.Hostinfo().RoutableIPs {
if r == wantedPrefix { /* safe to enable */ }
} Try / catch
if err := hsdb.EnableRoute(tx, nodeID, routeID); err != nil {
if errors.Is(err, db.ErrNodeRouteIsNotAvailable) {
return fmt.Errorf("node %d does not advertise this route; run `tailscale set --advertise-routes=%s` first", nodeID, prefix)
}
return err
} Prevention
- Always enable routes from the IDs shown by `headscale routes list`
- Provision advertise-routes on nodes before approval automation runs
- Re-check advertised routes after node reboots/reinstalls
When it happens
Trigger: Calling `headscale routes enable` with a route/prefix the node has never advertised via `tailscale up --advertise-routes=...`; enabling by route ID that belongs to another node; node changed its advertised routes so the old prefix no longer exists.
Common situations: Approving a route typo'd in the CLI; the node dropped the advertisement (rebooted without --advertise-routes) and an automation still tries to approve the old prefix.
Related errors
- node name is not unique
- node not found
- failed to convert node interface
- invalid auto approver format
- node not found
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/12bf1f99de7418d7.
Report an issue: GitHub.