wavetermdev/waveterm · error

cannot bind source route %q to link %d (link is not a leaf)

Error message

cannot bind source route %q to link %d (link is not a leaf)

What it means

When isSourceRoute is true, bindRouteLocally is binding the link's own source route — the route the leaf 'is'. Source routes may only be bound to links of kind LinkKind_Leaf. If the link's kind is LinkKind_Router (or anything else), the bind is rejected because a router-kind link represents an intermediary, not a leaf endpoint owning a source route.

Source

Thrown at pkg/wshutil/wshrouter.go:785

func (router *WshRouter) bindRouteLocally(linkId baseds.LinkId, routeId string, isSourceRoute bool) error {
	if linkId == baseds.NoLinkId {
		return fmt.Errorf("cannot bindroute %q to NoLinkId", routeId)
	}
	if !isBindableRouteId(routeId) {
		return fmt.Errorf("router cannot register %q route (invalid routeid)", routeId)
	}
	router.lock.Lock()
	defer router.lock.Unlock()
	lm := router.linkMap[linkId]
	if lm == nil {
		return fmt.Errorf("cannot bind route %q, no link with id %d found", routeId, linkId)
	}
	if !lm.trusted {
		return fmt.Errorf("cannot bind route %q, link %d is not trusted", routeId, linkId)
	}
	if isSourceRoute {
		if lm.linkKind != LinkKind_Leaf {
			return fmt.Errorf("cannot bind source route %q to link %d (link is not a leaf)", routeId, linkId)
		}
		if lm.sourceRouteId != "" && lm.sourceRouteId != routeId {
			return fmt.Errorf("cannot bind source route %q to link %d (link already has source route %q)", routeId, linkId, lm.sourceRouteId)
		}
		lm.sourceRouteId = routeId
	} else {
		if lm.linkKind != LinkKind_Router {
			return fmt.Errorf("cannot bind route %q to link %d (link is not a router)", routeId, linkId)
		}
	}
	router.routeMap[routeId] = linkId
	return nil
}

func (router *WshRouter) bindRoute(linkId baseds.LinkId, routeId string, isSourceRoute bool) error {
	err := router.bindRouteLocally(linkId, routeId, isSourceRoute)
	if err != nil {
		return err

View on GitHub (pinned to a4447c1563)

Solutions

  1. For source routes, register/trust the link as a leaf (RegisterTrustedLeaf) so its kind is LinkKind_Leaf
  2. If the link really is a router, call bindRoute with isSourceRoute=false
  3. Re-register the link with the correct kind when its role changes, then rebind routes

Example fix

// before
linkId := router.RegisterRouterLink(rpc) // LinkKind_Router
err := router.bindRoute(linkId, routeId, true) // source route on non-leaf
// after
linkId, err := router.RegisterTrustedLeaf(rpc, routeId)
Defensive patterns

Strategy: validation

Validate before calling

// source routes only on leaf links — use the leaf registration API
linkId, err := router.RegisterTrustedLeaf(rpc, routeId)
if err != nil {
    return err
}

Try / catch

err := router.bindRoute(linkId, routeId, true)
if err != nil && strings.Contains(err.Error(), "not a leaf") {
    // link is router-kind; re-register as a leaf or use isSourceRoute=false
}

Prevention

When it happens

Trigger: Calling WshRouter.bindRoute(linkId, routeId, true) (isSourceRoute=true) against a link whose linkKind is LinkKind_Router, e.g. a link registered/trusted as a router (RegisterRouterLink + LinkKind_Router) rather than as a leaf (RegisterTrustedLeaf).

Common situations: Using RegisterRouterLink for what is actually a leaf endpoint and then binding its source route; trusting a link with LinkKind_Router by mistake in custom router wiring; switching a connection from leaf to router (or vice versa) without re-registering, then rebinding old routes.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/48617e6ced1fe804. Report an issue: GitHub.