wavetermdev/waveterm · error
cannot bind route %q, no link with id %d found
Error message
cannot bind route %q, no link with id %d found
What it means
After validating routeId, bindRouteLocally looks up the link in router.linkMap. If no link with the given linkId has been registered on this router, the bind cannot proceed because there is no connection to associate the route with. This is the router's 'unknown link' check.
Source
Thrown at pkg/wshutil/wshrouter.go:778
router.unannounceUpstream(routeId)
if router.IsRootRouter() {
router.unsubscribeFromBroker(routeId)
}
return nil
}
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] = linkIdView on GitHub (pinned to a4447c1563)
Solutions
- Re-register the link (RegisterUntrustedLink/RegisterRouterLink) and use the freshly returned linkId
- Ensure the bind happens while the link is still live; reorder so bind occurs before any unregister/teardown
- Use the same WshRouter instance that owns the link; don't pass link ids across router instances
Example fix
// before err := router.bindRoute(oldLinkId, routeId, true) // oldLinkId unregistered after reconnect // after linkId := router.RegisterUntrustedLink(rpc) err := router.bindRoute(linkId, routeId, true)
Defensive patterns
Strategy: validation
Validate before calling
// bind only with a linkId this router returned and has not unregistered linkId := router.RegisterUntrustedLink(rpc) err := router.bindRoute(linkId, routeId, true)
Try / catch
err := router.bindRoute(staleLinkId, routeId, isSource)
if err != nil && strings.Contains(err.Error(), "no link with id") {
// link was unregistered; re-register the link and retry the bind
} Prevention
- Always bind routes immediately after registering the link, using the returned id
- On reconnect, discard old linkIds and rebind with new ones
- Don't share linkIds across WshRouter instances
When it happens
Trigger: Calling WshRouter.bindRoute with a linkId that was never registered via RegisterUntrustedLink/RegisterRouterLink/RegisterTrustedLeaf, or that was already removed by UnregisterLink.
Common situations: Binding a route after the connection dropped and UnregisterLink ran; using a linkId from a different WshRouter instance; reusing a stale cached linkId across a reconnect; race between route binding and concurrent link unregistration.
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, link %d is not trusted
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/5c7aaa6debfdb027.
Report an issue: GitHub.