{"record":{"id":"05c01fc59c2643ad","repo":"wavetermdev/waveterm","slug":"error-validating-token-w","errorCode":null,"errorMessage":"error validating token: %w","messagePattern":"error validating token: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wshutil/wshrouter_controlimpl.go","lineNumber":103,"sourceCode":"\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"setpeerinfo only valid for proxy connections\")\n}\n\nfunc (impl *WshRouterControlImpl) AuthenticateCommand(ctx context.Context, data string) (wshrpc.CommandAuthenticateRtnData, error) {\n\thandler := GetRpcResponseHandlerFromContext(ctx)\n\tif handler == nil {\n\t\treturn wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf(\"no response handler in context\")\n\t}\n\tlinkId := handler.GetIngressLinkId()\n\tif linkId == baseds.NoLinkId {\n\t\treturn wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf(\"no ingress link found\")\n\t}\n\n\tnewCtx, err := ValidateAndExtractRpcContextFromToken(data)\n\tif err != nil {\n\t\tlog.Printf(\"wshrouter authenticate error linkid=%d: %v\", linkId, err)\n\t\treturn wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf(\"error validating token: %w\", err)\n\t}\n\trouteId, err := validateRpcContextFromAuth(newCtx)\n\tif err != nil {\n\t\treturn wshrpc.CommandAuthenticateRtnData{}, err\n\t}\n\n\trtnData := wshrpc.CommandAuthenticateRtnData{RouteId: routeId}\n\tif newCtx.IsRouter {\n\t\tlog.Printf(\"wshrouter authenticate success linkid=%d (router)\", linkId)\n\t\timpl.Router.trustLink(linkId, LinkKind_Router)\n\t} else {\n\t\tlog.Printf(\"wshrouter authenticate success linkid=%d routeid=%q\", linkId, routeId)\n\t\timpl.Router.trustLink(linkId, LinkKind_Leaf)\n\t\timpl.Router.bindRoute(linkId, routeId, true)\n\t}\n\n\treturn rtnData, nil\n}","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshutil/wshrouter_controlimpl.go#L85-L121","documentation":"AuthenticateCommand wraps any failure from ValidateAndExtractRpcContextFromToken (which parses the JWT and extracts the embedded RpcContext) with the message \"error validating token: %w\". The router received an auth token on a link but could not parse or validate it as a valid JWT carrying a usable RpcContext. The underlying cause is preserved via %w, so check the wrapped message (e.g. signature mismatch, malformed JWT, missing context claim).","triggerScenarios":"Calling AuthenticateCommand (via the wsh control RPC) with `data` set to a token string that is not a valid JWT, is signed with a different key than the router expects, has expired, or does not carry a parseable RpcContext claim.","commonSituations":"wsh clients connecting with a stale token after the server regenerated its keys; copy/paste truncating the JWT; a client built against a different Wave version encoding an incompatible RpcContext; env var WAVETERM_TOKEN pointing at an old token.","solutions":["Unwrap and read the inner error (Go: errors.Unwrap / %v of the result) to see whether the JWT is malformed, has a bad signature, or lacks the RpcContext claim.","Obtain a fresh token from the root router / wave server (e.g. re-run the connection/token-swap flow) instead of reusing a cached one.","Verify client and server are the same Wave Terminal version so the JWT claims (RpcContext shape) match.","Check that the full, untruncated token string is passed as the Authenticate command data (no whitespace/newlines added by shell quoting)."],"exampleFix":"// before\nerr := client.Authenticate(ctx, os.Getenv(\"OLD_WAVETOKEN\"))\n// after\ntoken := os.Getenv(\"WAVETERM_TOKEN\")\nif token == \"\" {\n    return fmt.Errorf(\"WAVETERM_TOKEN not set; get a fresh token from the wave server\")\n}\nerr := client.Authenticate(ctx, strings.TrimSpace(token))","handlingStrategy":"validation","validationCode":"if token == \"\" || strings.ContainsAny(token, \" \\n\\r\\t\") {\n    return fmt.Errorf(\"token empty or contains whitespace; re-mint before authenticating\")\n}\n// optionally check JWT has 3 segments\nif len(strings.Split(token, \".\")) != 3 {\n    return fmt.Errorf(\"token is not a well-formed JWT\")\n}","typeGuard":null,"tryCatchPattern":"// Go\nrtn, err := client.Authenticate(ctx, token)\nif err != nil {\n    var wrappedErr error\n    if errors.Unwrap(err) != nil {\n        wrappedErr = errors.Unwrap(err)\n        log.Printf(\"token validation root cause: %v\", wrappedErr)\n    }\n    return fmt.Errorf(\"authenticate failed: %w\", err)\n}","preventionTips":["Always fetch a fresh token from the server rather than caching across restarts","Trim whitespace/newlines from tokens read from env vars or files","Keep client and server Wave versions in sync"],"tags":["auth","jwt","token-validation","rpc"],"backgroundTag":"jwt-token-invalid","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}