wavetermdev/waveterm · error
cannot bind source route %q to link %d (link already has sou
Error message
cannot bind source route %q to link %d (link already has source route %q)
What it means
A leaf link can own exactly one source route. When binding a source route, bindRouteLocally checks lm.sourceRouteId: if it is already set to a different routeId, the bind is refused to prevent a single leaf from claiming two source routes (which would make inbound routing ambiguous).
Source
Thrown at pkg/wshutil/wshrouter.go:788
}
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
}
lm := router.getLinkMeta(linkId)
if lm != nil {View on GitHub (pinned to a4447c1563)
Solutions
- Unbind the existing source route (unbindRoute with the old routeId) before binding the new one
- If the leaf should keep its original route, bind with the existing lm.sourceRouteId instead of a new one
- Register a separate link per leaf route — one leaf link maps to one source route
Example fix
// before
err := router.bindRoute(linkId, newRouteId, true) // leaf already has oldRouteId
// after
if err := router.unbindRoute(linkId, oldRouteId); err != nil {
return err
}
err := router.bindRoute(linkId, newRouteId, true) Defensive patterns
Strategy: validation
Validate before calling
// one leaf link = one source route; unbind the old route before binding a new one
if oldRouteId != "" && oldRouteId != newRouteId {
if err := router.unbindRoute(linkId, oldRouteId); err != nil {
return err
}
}
err := router.bindRoute(linkId, newRouteId, true) Try / catch
err := router.bindRoute(linkId, routeId, true)
if err != nil && strings.Contains(err.Error(), "already has source route") {
// unbind the existing source route or reuse it, then retry
} Prevention
- Never share one AbstractRpcClient between multiple leaf routes
- On reconnect, unbind the stale route before rebinding with a new routeId
- Track each leaf's bound source route to avoid surprise conflicts
When it happens
Trigger: Calling WshRouter.bindRoute(linkId, routeId, true) on a leaf whose sourceRouteId is already bound to a different route — e.g. rebinding after a route rename, or reusing one registered leaf rpc for two routes.
Common situations: Re-registering/rebinding a leaf connection after reconnect with a new routeId while the old source route is still attached; sharing one AbstractRpcClient between two leaves; a stale routeannounce re-binding an old route onto a leaf that has moved on.
Related errors
- invalid routeid %q
- cannot unbind %q to NoLinkId
- cannot bindroute %q to NoLinkId
- router cannot register %q route (invalid routeid)
- cannot bind route %q, no link with id %d found
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/4c05929881d4d0b6.
Report an issue: GitHub.