{"record":{"id":"293c6fcebfa4be39","repo":"wavetermdev/waveterm","slug":"cannot-bindroute-q-to-nolinkid","errorCode":null,"errorMessage":"cannot bindroute %q to NoLinkId","messagePattern":"cannot bindroute %q to NoLinkId","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wshutil/wshrouter.go","lineNumber":769,"sourceCode":"func (router *WshRouter) unbindRoute(linkId baseds.LinkId, routeId string) error {\n\terr := router.unbindRouteLocally(linkId, routeId)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlm := router.getLinkMeta(linkId)\n\tif lm != nil {\n\t\tlog.Printf(\"wshrouter unbind route %q from %s\", routeId, lm.Name())\n\t}\n\trouter.unannounceUpstream(routeId)\n\tif router.IsRootRouter() {\n\t\trouter.unsubscribeFromBroker(routeId)\n\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 {","sourceCodeStart":751,"sourceCodeEnd":787,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshutil/wshrouter.go#L751-L787","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\nerr := router.bindRoute(linkId, routeId, true)\n// after\nif linkId == baseds.NoLinkId {\n    return fmt.Errorf(\"cannot bind route %q: link not registered yet\", routeId)\n}\nerr := router.bindRoute(linkId, routeId, true)","handlingStrategy":"validation","validationCode":"if linkId == baseds.NoLinkId {\n    return fmt.Errorf(\"link not registered; cannot bind %q\", routeId)\n}\nerr := router.bindRoute(linkId, routeId, true)","typeGuard":"func hasLink(id baseds.LinkId) bool { return id != baseds.NoLinkId }\nif !hasLink(linkId) { return }","tryCatchPattern":"err := router.bindRoute(linkId, routeId, isSource)\nif err != nil && strings.Contains(err.Error(), \"NoLinkId\") {\n    // re-register the link, then retry the bind once\n}","preventionTips":["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"],"tags":["go","wshrouter","routing","invalid-argument"],"backgroundTag":"invalid-link-id","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}