{"record":{"id":"8fd901ed667f8a5d","repo":"wavetermdev/waveterm","slug":"invalid-context-must-have-a-routeid","errorCode":null,"errorMessage":"invalid context, must have a routeid","messagePattern":"invalid context, must have a routeid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wshutil/wshrouter_controlimpl.go","lineNumber":303,"sourceCode":"\tlog.Printf(\"wshrouter authenticate-jobmanager success linkid=%d jobid=%q routeid=%q\", linkId, data.JobId, routeId)\n\timpl.Router.trustLink(linkId, LinkKind_Leaf)\n\timpl.Router.bindRoute(linkId, routeId, true)\n\n\treturn nil\n}\n\nfunc validateRpcContextFromAuth(newCtx *wshrpc.RpcContext) (string, error) {\n\tif newCtx == nil {\n\t\treturn \"\", fmt.Errorf(\"no context found in jwt token\")\n\t}\n\tif newCtx.IsRouter && newCtx.RouteId != \"\" {\n\t\treturn \"\", fmt.Errorf(\"invalid context, router cannot have a routeid\")\n\t}\n\tif newCtx.IsRouter && newCtx.ProcRoute {\n\t\treturn \"\", fmt.Errorf(\"invalid context, router cannot have a proc-route\")\n\t}\n\tif !newCtx.IsRouter && newCtx.RouteId == \"\" && !newCtx.ProcRoute {\n\t\treturn \"\", fmt.Errorf(\"invalid context, must have a routeid\")\n\t}\n\tif newCtx.IsRouter {\n\t\treturn \"\", nil\n\t}\n\treturn newCtx.GenerateRouteId(), nil\n}\n","sourceCodeStart":285,"sourceCodeEnd":310,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshutil/wshrouter_controlimpl.go#L285-L310","documentation":"This error comes from validateRpcContextFromAuth, which normalizes an RpcContext after authentication. A non-router context must carry either a RouteId or a ProcRoute marker so the wsh router knows where to send RPCs; otherwise it is unaddressable. The library throws it to prevent routing RPCs to an unknown destination.","triggerScenarios":"Calling AuthenticateCommand or extractTokenData with an auth/context whose IsRouter is false, ProcRoute is false, and RouteId is empty string.","commonSituations":"A client connects over a wsh pipe/route without setting routeid in its metadata; a manually constructed RpcContext missing RouteId; a server-side component forwarding auth for a connection that never registered a route; version mismatches where old clients omit routeid.","solutions":["Set RouteId on the RpcContext before authentication (e.g. ctx.RouteId = connRouteId)","If the context is a per-proc connection, set ProcRoute = true instead","If this connection really is a router, set IsRouter = true (then RouteId must be empty)","Verify the client handshake sends its routeid metadata (waveclient/wsh connect flags)"],"exampleFix":"// before\nctx := wshutil.RpcContext{} // no RouteId\n// after\nctx := wshutil.RpcContext{RouteId: routeId}","handlingStrategy":"validation","validationCode":"func validRpcContext(ctx wshutil.RpcContext) bool {\n\treturn ctx.IsRouter || ctx.ProcRoute || ctx.RouteId != \"\"\n}\nif !validRpcContext(ctx) {\n\tctx.RouteId = myRouteId // assign before authenticating\n}","typeGuard":"func isAddressable(ctx wshutil.RpcContext) bool {\n\treturn ctx.IsRouter || ctx.ProcRoute || ctx.RouteId != \"\"\n}","tryCatchPattern":null,"preventionTips":["Always populate RouteId when creating a client-side RpcContext","Use ProcRoute for per-proc connections instead of leaving fields empty","Log the RpcContext before AuthenticateCommand during development","Keep client handshake code in one helper so routeid is never forgotten"],"tags":["wsh","rpc","routing","validation"],"backgroundTag":"missing-route-id","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}