{"record":{"id":"0ce0ac2d93bdc6e4","repo":"wavetermdev/waveterm","slug":"no-source-in-routeannounce","errorCode":null,"errorMessage":"no source in routeannounce","messagePattern":"no source in routeannounce","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wshutil/wshrouter_controlimpl.go","lineNumber":28,"sourceCode":"\n\t\"github.com/wavetermdev/waveterm/pkg/baseds\"\n\t\"github.com/wavetermdev/waveterm/pkg/util/shellutil\"\n\t\"github.com/wavetermdev/waveterm/pkg/util/utilfn\"\n\t\"github.com/wavetermdev/waveterm/pkg/waveobj\"\n\t\"github.com/wavetermdev/waveterm/pkg/wshrpc\"\n\t\"github.com/wavetermdev/waveterm/pkg/wstore\"\n)\n\ntype WshRouterControlImpl struct {\n\tRouter *WshRouter\n}\n\nfunc (impl *WshRouterControlImpl) WshServerImpl() {}\n\nfunc (impl *WshRouterControlImpl) RouteAnnounceCommand(ctx context.Context) error {\n\tsource := GetRpcSourceFromContext(ctx)\n\tif source == \"\" {\n\t\treturn fmt.Errorf(\"no source in routeannounce\")\n\t}\n\thandler := GetRpcResponseHandlerFromContext(ctx)\n\tif handler == nil {\n\t\treturn fmt.Errorf(\"no response handler in context\")\n\t}\n\tlinkId := handler.GetIngressLinkId()\n\tif linkId == baseds.NoLinkId {\n\t\treturn fmt.Errorf(\"no ingress link found\")\n\t}\n\treturn impl.Router.bindRoute(linkId, source, false)\n}\n\nfunc (impl *WshRouterControlImpl) RouteUnannounceCommand(ctx context.Context) error {\n\tsource := GetRpcSourceFromContext(ctx)\n\tif source == \"\" {\n\t\treturn fmt.Errorf(\"no source in routeunannounce\")\n\t}\n\thandler := GetRpcResponseHandlerFromContext(ctx)","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshutil/wshrouter_controlimpl.go#L10-L46","documentation":"RouteAnnounceCommand is the control-protocol handler for 'routeannounce' messages arriving on a router's control channel. The originating source route id is read from the RPC context via GetRpcSourceFromContext; announce handling needs to know which link announced the route, so if the context carries no source, the command is rejected. This indicates the announce was dispatched without proper routing context rather than a normal runtime condition.","triggerScenarios":"A routeannounce control message processed through a code path that did not set the RPC source in the context (missing source-route binding on the ingress link, or a hand-built context without the source annotation).","commonSituations":"Sending raw routeannounce control messages over a connection without the router having bound the sender's source route; custom/protocol-level clients speaking wsh control protocol without setting up context metadata; a router regression where ingress link id / source context is lost before the control impl runs.","solutions":["Ensure the sending side has its source route bound (bindRouteLocally with isSourceRoute=true) before emitting routeannounce, so the context carries the source","Send routeannounce through the normal WshRouter/WshClient control path rather than constructing the RPC context manually","Check that GetRpcResponseHandlerFromContext / ingress link setup ran for the connection before the announce is dispatched"],"exampleFix":"// before\nctx := context.Background()\nerr := wshclient.RouteAnnounceCommand(ctx, client) // no source in ctx\n// after\nctx = context.WithValue(context.Background(), RpcSourceKey{}, sourceRouteId)\nerr = wshclient.RouteAnnounceCommand(ctx, client)","handlingStrategy":"try-catch","validationCode":"source := wshutil.GetRpcSourceFromContext(ctx)\nif source == \"\" {\n    return fmt.Errorf(\"cannot announce: no source route bound\")\n}\nerr := wshclient.RouteAnnounceCommand(ctx, client)","typeGuard":"func hasRpcSource(ctx context.Context) bool {\n    return wshutil.GetRpcSourceFromContext(ctx) != \"\"\n}\nif !hasRpcSource(ctx) { return }","tryCatchPattern":"err := wshclient.RouteAnnounceCommand(ctx, client)\nif err != nil && strings.Contains(err.Error(), \"no source in routeannounce\") {\n    // the ingress link's source route was never bound; bind it before announcing\n}","preventionTips":["Always bind the sender's source route before sending routeannounce control messages","Use wshclient/rpc helper paths instead of hand-constructing RPC contexts","Verify ingress link setup (response handler, ingress link id) on every new connection before control traffic"],"tags":["go","wshrouter","routing","control-protocol"],"backgroundTag":"missing-rpc-source-context","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}