wavetermdev/waveterm · error

cannot bindroute 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.

Solutions

  1. Ensure the link is registered first and use the returned linkId from RegisterUntrustedLink/RegisterRouterLink
  2. Guard the bind call: skip or return early when linkId == baseds.NoLinkId
  3. 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

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


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

Appendix: 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)