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 errView on GitHub (pinned to a4447c1563)
Solutions
- For source routes, register/trust the link as a leaf (RegisterTrustedLeaf) so its kind is LinkKind_Leaf
- If the link really is a router, call bindRoute with isSourceRoute=false
- 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
- Match link kind to role: leaves get source routes, routers get forwarded routes
- Use RegisterTrustedLeaf so kind and source-route binding stay consistent
- Re-register links when a connection changes role
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
- cannot bind route %q to link %d (link is not a router)
- invalid routeid %q
- cannot unbind %q to NoLinkId
- cannot bindroute %q to NoLinkId
- router cannot register %q route (invalid routeid)
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/48617e6ced1fe804.
Report an issue: GitHub.