wavetermdev/waveterm · error

cannot bind route %q, link %d is not trusted

Error message

cannot bind route %q, link %d is not trusted

What it means

bindRouteLocally only allows routes to be bound to trusted links. After finding the link metadata (lm), it checks lm.trusted; if the link was registered as untrusted (e.g. via RegisterUntrustedLink without a subsequent trustLink call), binding a route to it is refused, since untrusted links must not claim routes.

Source

Thrown at pkg/wshutil/wshrouter.go:781

	}
	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] = linkId
	return nil
}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Mark the link trusted before binding: call the router's trust mechanism (e.g. RegisterTrustedLeaf for leaves, or the trust step used for router links)
  2. If the link should stay untrusted, do not bind a route to it and route messages by linkId instead
  3. On reconnect, re-run the trust flow for the new link before rebinding routes

Example fix

// before
linkId := router.RegisterUntrustedLink(rpc)
err := router.bindRoute(linkId, routeId, true) // link not trusted
// after
linkId, err := router.RegisterTrustedLeaf(rpc, routeId)
if err != nil {
    return err
}
Defensive patterns

Strategy: validation

Validate before calling

// prefer the combined API which registers AND trusts AND binds
linkId, err := router.RegisterTrustedLeaf(rpc, routeId)
if err != nil {
    return err
}

Try / catch

err := router.bindRoute(linkId, routeId, isSource)
if err != nil && strings.Contains(err.Error(), "is not trusted") {
    // trust the link first, then retry the bind
}

Prevention

When it happens

Trigger: Calling WshRouter.bindRoute for a link registered with RegisterUntrustedLink that was never trusted via trustLink/TrustLink/MarkLinkAsTrusted, then attempting to bind a route to it.

Common situations: Registering a downstream connection as untrusted (block-level wsh connections) and then trying to claim a stable route on it; forgetting the trust step in a custom router setup; link trust state lost after reconnect so the new link is untrusted while old code still binds routes.

Related errors


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