{"record":{"id":"0caeb4eca8981932","repo":"wavetermdev/waveterm","slug":"cannot-bind-route-q-link-d-is-not-trusted","errorCode":null,"errorMessage":"cannot bind route %q, link %d is not trusted","messagePattern":"cannot bind route %q, link (.+?) is not trusted","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wshutil/wshrouter.go","lineNumber":781,"sourceCode":"\t}\n\treturn nil\n}\n\nfunc (router *WshRouter) bindRouteLocally(linkId baseds.LinkId, routeId string, isSourceRoute bool) error {\n\tif linkId == baseds.NoLinkId {\n\t\treturn fmt.Errorf(\"cannot bindroute %q to NoLinkId\", routeId)\n\t}\n\tif !isBindableRouteId(routeId) {\n\t\treturn fmt.Errorf(\"router cannot register %q route (invalid routeid)\", routeId)\n\t}\n\trouter.lock.Lock()\n\tdefer router.lock.Unlock()\n\tlm := router.linkMap[linkId]\n\tif lm == nil {\n\t\treturn fmt.Errorf(\"cannot bind route %q, no link with id %d found\", routeId, linkId)\n\t}\n\tif !lm.trusted {\n\t\treturn fmt.Errorf(\"cannot bind route %q, link %d is not trusted\", routeId, linkId)\n\t}\n\tif isSourceRoute {\n\t\tif lm.linkKind != LinkKind_Leaf {\n\t\t\treturn fmt.Errorf(\"cannot bind source route %q to link %d (link is not a leaf)\", routeId, linkId)\n\t\t}\n\t\tif lm.sourceRouteId != \"\" && lm.sourceRouteId != routeId {\n\t\t\treturn fmt.Errorf(\"cannot bind source route %q to link %d (link already has source route %q)\", routeId, linkId, lm.sourceRouteId)\n\t\t}\n\t\tlm.sourceRouteId = routeId\n\t} else {\n\t\tif lm.linkKind != LinkKind_Router {\n\t\t\treturn fmt.Errorf(\"cannot bind route %q to link %d (link is not a router)\", routeId, linkId)\n\t\t}\n\t}\n\trouter.routeMap[routeId] = linkId\n\treturn nil\n}\n","sourceCodeStart":763,"sourceCodeEnd":799,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshutil/wshrouter.go#L763-L799","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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)","If the link should stay untrusted, do not bind a route to it and route messages by linkId instead","On reconnect, re-run the trust flow for the new link before rebinding routes"],"exampleFix":"// before\nlinkId := router.RegisterUntrustedLink(rpc)\nerr := router.bindRoute(linkId, routeId, true) // link not trusted\n// after\nlinkId, err := router.RegisterTrustedLeaf(rpc, routeId)\nif err != nil {\n    return err\n}","handlingStrategy":"validation","validationCode":"// prefer the combined API which registers AND trusts AND binds\nlinkId, err := router.RegisterTrustedLeaf(rpc, routeId)\nif err != nil {\n    return err\n}","typeGuard":null,"tryCatchPattern":"err := router.bindRoute(linkId, routeId, isSource)\nif err != nil && strings.Contains(err.Error(), \"is not trusted\") {\n    // trust the link first, then retry the bind\n}","preventionTips":["Use RegisterTrustedLeaf for leaves instead of RegisterUntrustedLink + manual bind","Complete the trust step before any route binding in custom router wiring","Re-run the trust flow after every reconnect"],"tags":["go","wshrouter","routing","trust"],"backgroundTag":"untrusted-link","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}