{"record":{"id":"9da0df2bc9e0c502","repo":"wavetermdev/waveterm","slug":"invalid-context-router-cannot-have-a-routeid","errorCode":null,"errorMessage":"invalid context, router cannot have a routeid","messagePattern":"invalid context, router cannot have a routeid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wshutil/wshrouter_controlimpl.go","lineNumber":297,"sourceCode":"\t\t\tlog.Printf(\"wshrouter authenticate-jobmanager error linkid=%d jobid=%q: failed to verify job auth token: %v\", linkId, data.JobId, err)\n\t\t\treturn fmt.Errorf(\"failed to verify job auth token: %w\", err)\n\t\t}\n\t}\n\n\trouteId := MakeJobRouteId(data.JobId)\n\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":279,"sourceCodeEnd":310,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshutil/wshrouter_controlimpl.go#L279-L310","documentation":"Sentinel/creation-time validation: a router (as opposed to an RPC endpoint) context must not carry a route ID. This error is returned when building or validating a router context that unexpectedly has a route ID set, which would corrupt routing semantics.","triggerScenarios":"Authenticating with a token whose RpcContext has IsRouter:true and RouteId != \"\".","commonSituations":"Client code confusing its own route id with router identity when building the token; copy-pasted context structs between leaf and router clients.","solutions":["Set RouteId to \"\" when IsRouter is true in the token's RpcContext","Fix token-minting code to build the router context without a route id","Use the correct context constructor for router vs leaf connections"],"exampleFix":"// before\nctx := wshrpc.RpcContext{IsRouter: true, RouteId: myRouteId}\n// after\nctx := wshrpc.RpcContext{IsRouter: true, RouteId: \"\"}","handlingStrategy":"validation","validationCode":"if newCtx.IsRouter && newCtx.RouteId != \"\" {\n    return fmt.Errorf(\"router token context must not set RouteId\")\n}","typeGuard":"func validRouterContext(c *wshrpc.RpcContext) bool {\n    return c.IsRouter && c.RouteId == \"\" && !c.ProcRoute\n}","tryCatchPattern":"if _, err := validateRpcContextFromAuth(newCtx); err != nil {\n    // rebuild and re-mint the token with a router context (RouteId empty)\n}","preventionTips":["Use dedicated constructors for router vs leaf RpcContext","Unit-test token minting for both roles","Never hand-merge leaf and router context fields"],"tags":["wsh","jwt","validation"],"backgroundTag":"jwt-context-invalid","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}