{"record":{"id":"48617e6ced1fe804","repo":"wavetermdev/waveterm","slug":"cannot-bind-source-route-q-to-link-d-link-is-no","errorCode":null,"errorMessage":"cannot bind source route %q to link %d (link is not a leaf)","messagePattern":"cannot bind source route %q to link (.+?) \\(link is not a leaf\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wshutil/wshrouter.go","lineNumber":785,"sourceCode":"func (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\nfunc (router *WshRouter) bindRoute(linkId baseds.LinkId, routeId string, isSourceRoute bool) error {\n\terr := router.bindRouteLocally(linkId, routeId, isSourceRoute)\n\tif err != nil {\n\t\treturn err","sourceCodeStart":767,"sourceCodeEnd":803,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshutil/wshrouter.go#L767-L803","documentation":"When isSourceRoute is true, bindRouteLocally is binding the link's own source route — the route the leaf 'is'. Source routes may only be bound to links of kind LinkKind_Leaf. If the link's kind is LinkKind_Router (or anything else), the bind is rejected because a router-kind link represents an intermediary, not a leaf endpoint owning a source route.","triggerScenarios":"Calling WshRouter.bindRoute(linkId, routeId, true) (isSourceRoute=true) against a link whose linkKind is LinkKind_Router, e.g. a link registered/trusted as a router (RegisterRouterLink + LinkKind_Router) rather than as a leaf (RegisterTrustedLeaf).","commonSituations":"Using RegisterRouterLink for what is actually a leaf endpoint and then binding its source route; trusting a link with LinkKind_Router by mistake in custom router wiring; switching a connection from leaf to router (or vice versa) without re-registering, then rebinding old routes.","solutions":["For source routes, register/trust the link as a leaf (RegisterTrustedLeaf) so its kind is LinkKind_Leaf","If the link really is a router, call bindRoute with isSourceRoute=false","Re-register the link with the correct kind when its role changes, then rebind routes"],"exampleFix":"// before\nlinkId := router.RegisterRouterLink(rpc) // LinkKind_Router\nerr := router.bindRoute(linkId, routeId, true) // source route on non-leaf\n// after\nlinkId, err := router.RegisterTrustedLeaf(rpc, routeId)","handlingStrategy":"validation","validationCode":"// source routes only on leaf links — use the leaf registration API\nlinkId, err := router.RegisterTrustedLeaf(rpc, routeId)\nif err != nil {\n    return err\n}","typeGuard":null,"tryCatchPattern":"err := router.bindRoute(linkId, routeId, true)\nif err != nil && strings.Contains(err.Error(), \"not a leaf\") {\n    // link is router-kind; re-register as a leaf or use isSourceRoute=false\n}","preventionTips":["Match link kind to role: leaves get source routes, routers get forwarded routes","Use RegisterTrustedLeaf so kind and source-route binding stay consistent","Re-register links when a connection changes role"],"tags":["go","wshrouter","routing","link-kind"],"backgroundTag":"wrong-link-kind","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}