wavetermdev/waveterm · error
cannot bindroute %q to NoLinkId
Error message
cannot bindroute %q to NoLinkId
What it means
bindRouteLocally maps a routeId onto a link in the router's routeMap. Since NoLinkId is the sentinel for 'no link', binding a route to it would create a corrupt routing entry, so the bind is rejected immediately as the first validation in the function.
Source
Thrown at pkg/wshutil/wshrouter.go:769
func (router *WshRouter) unbindRoute(linkId baseds.LinkId, routeId string) error {
err := router.unbindRouteLocally(linkId, routeId)
if err != nil {
return err
}
lm := router.getLinkMeta(linkId)
if lm != nil {
log.Printf("wshrouter unbind route %q from %s", routeId, lm.Name())
}
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 {View on GitHub (pinned to a4447c1563)
Solutions
- Ensure the link is registered first and use the returned linkId from RegisterUntrustedLink/RegisterRouterLink
- Guard the bind call: skip or return early when linkId == baseds.NoLinkId
- Fix the lookup that yields NoLinkId (check error handling around link creation)
Example fix
// before
err := router.bindRoute(linkId, routeId, true)
// after
if linkId == baseds.NoLinkId {
return fmt.Errorf("cannot bind route %q: link not registered yet", routeId)
}
err := router.bindRoute(linkId, routeId, true) Defensive patterns
Strategy: validation
Validate before calling
if linkId == baseds.NoLinkId {
return fmt.Errorf("link not registered; cannot bind %q", routeId)
}
err := router.bindRoute(linkId, routeId, true) Type guard
func hasLink(id baseds.LinkId) bool { return id != baseds.NoLinkId }
if !hasLink(linkId) { return } Try / catch
err := router.bindRoute(linkId, routeId, isSource)
if err != nil && strings.Contains(err.Error(), "NoLinkId") {
// re-register the link, then retry the bind once
} Prevention
- Use the linkId returned by RegisterUntrustedLink/RegisterRouterLink immediately — never a zero value
- Register the link before binding any routes in setup code
- Check upstreamLinkId is set before using it in binds
When it happens
Trigger: Calling WshRouter.bindRoute / bindRouteLocally with linkId == baseds.NoLinkId — e.g. the link id came from an unset variable, a failed lookup, or a field that defaulted to zero before the link was registered.
Common situations: Registering a route during connection setup before RegisterUntrustedLink/RegisterRouterLink returned the real id; passing router.upstreamLinkId when no upstream is connected (it resets to NoLinkId after UnregisterLink); test code that forgets to register the link first.
Related errors
- invalid routeid %q
- cannot unbind %q to NoLinkId
- router cannot register %q route (invalid routeid)
- cannot bind route %q, no link with id %d found
- cannot bind route %q, link %d is not trusted
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/293c6fcebfa4be39.
Report an issue: GitHub.