{"record":{"id":"2818914d06206c59","repo":"wavetermdev/waveterm","slug":"no-rpccontext-in-token-response","errorCode":null,"errorMessage":"no rpccontext in token response","messagePattern":"no rpccontext in token response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wshutil/wshrouter_controlimpl.go","lineNumber":205,"sourceCode":"\t\t}\n\t} else {\n\t\twshRpc := GetWshRpcFromContext(ctx)\n\t\tif wshRpc == nil {\n\t\t\treturn wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf(\"no wshrpc in context\")\n\t\t}\n\t\trespData, err := wshRpc.SendRpcRequest(wshrpc.Command_AuthenticateTokenVerify, data, &wshrpc.RpcOpts{Route: ControlRootRoute})\n\t\tif err != nil {\n\t\t\tlog.Printf(\"wshrouter authenticate-token error linkid=%d: failed to verify token: %v\", linkId, err)\n\t\t\treturn wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf(\"failed to verify token: %w\", err)\n\t\t}\n\t\terr = utilfn.ReUnmarshal(&rtnData, respData)\n\t\tif err != nil {\n\t\t\treturn wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf(\"failed to unmarshal response: %w\", err)\n\t\t}\n\t}\n\n\tif rtnData.RpcContext == nil {\n\t\treturn wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf(\"no rpccontext in token response\")\n\t}\n\tif rtnData.RouteId == \"\" {\n\t\treturn wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf(\"no routeid in token response\")\n\t}\n\tlog.Printf(\"wshrouter authenticate-token success linkid=%d routeid=%q\", linkId, rtnData.RouteId)\n\timpl.Router.trustLink(linkId, LinkKind_Leaf)\n\timpl.Router.bindRoute(linkId, rtnData.RouteId, true)\n\n\treturn rtnData, nil\n}\n\nfunc (impl *WshRouterControlImpl) AuthenticateJobManagerVerifyCommand(ctx context.Context, data wshrpc.CommandAuthenticateJobManagerData) error {\n\tif !impl.Router.IsRootRouter() {\n\t\treturn fmt.Errorf(\"authenticatejobmanagerverify can only be called on root router\")\n\t}\n\n\tif data.JobId == \"\" {\n\t\treturn fmt.Errorf(\"no jobid in authenticatejobmanager message\")","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshutil/wshrouter_controlimpl.go#L187-L223","documentation":"The token verification succeeded structurally, but the returned CommandAuthenticateRtnData has a nil RpcContext. The router requires RpcContext to know the identity of the authenticated peer before trusting the link and binding its route; without it the handshake result is unusable.","triggerScenarios":"The root router's AuthenticateTokenVerifyCommand produced/returned a CommandAuthenticateRtnData with RpcContext unset — typically when the token payload was valid enough to verify but the root failed to populate the RpcContext (corrupt token data, or a root implementation that omits it).","commonSituations":"Custom or modified root implementations that return partial data; a token generated by an incompatible tool that lacks embedded rpc context; schema drift where RpcContext moved/renamed.","solutions":["Check the root router code path that builds CommandAuthenticateRtnData and ensure it sets RpcContext.","Regenerate the token with the current version of the wave client so embedded context is present.","Verify the token content passed to extractTokenData / verify includes the rpc context fields.","Confirm no version skew strips RpcContext during ReUnmarshal."],"exampleFix":"// before\nif rtnData.RpcContext == nil {\n    return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf(\"no rpccontext in token response\")\n}\n// after (caller-side check before trusting result)\nif rtnData.RpcContext == nil {\n    return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf(\"no rpccontext in token response: regenerate token with current client version\")\n}","handlingStrategy":"validation","validationCode":"// pre-validate token embeds an rpc context before handshake\nif token == \"\" || !strings.Contains(token, \".\") {\n    return fmt.Errorf(\"malformed token: expected signed token with embedded context\")\n}","typeGuard":"func hasRpcContext(d wshrpc.CommandAuthenticateRtnData) bool {\n    return d.RpcContext != nil\n}","tryCatchPattern":"rtn, err := router.AuthenticateTokenCommand(ctx, data)\nif err != nil && strings.Contains(err.Error(), \"no rpccontext in token response\") {\n    return fmt.Errorf(\"regenerate token with current client version: %w\", err)\n}","preventionTips":["Generate tokens only with the current official wave client.","Treat a nil RpcContext response as a poisoned token: regenerate, never retry with the same token.","Add a unit test asserting the root always populates RpcContext in verify responses."],"tags":["authentication","rpc","schema"],"backgroundTag":"missing-rpc-context-in-token-response","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}