wavetermdev/waveterm · error
cannot auth router via token
Error message
cannot auth router via token
What it means
extractTokenData explicitly rejects token entries whose RpcContext has IsRouter=true. Token-swap authentication is only meant for leaf connections (terminals, clients); router-level links must authenticate with a signed JWT through AuthenticateCommand instead. This is a policy guard, not a validation failure.
Source
Thrown at pkg/wshutil/wshrouter_controlimpl.go:133
log.Printf("wshrouter authenticate success linkid=%d routeid=%q", linkId, routeId)
impl.Router.trustLink(linkId, LinkKind_Leaf)
impl.Router.bindRoute(linkId, routeId, true)
}
return rtnData, nil
}
func extractTokenData(token string) (wshrpc.CommandAuthenticateRtnData, error) {
entry := shellutil.GetAndRemoveTokenSwapEntry(token)
if entry == nil {
return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("no token entry found")
}
_, err := validateRpcContextFromAuth(entry.RpcContext)
if err != nil {
return wshrpc.CommandAuthenticateRtnData{}, err
}
if entry.RpcContext.IsRouter {
return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("cannot auth router via token")
}
routeId := entry.RpcContext.GenerateRouteId()
if routeId == "" {
return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("no routeid")
}
return wshrpc.CommandAuthenticateRtnData{
RouteId: routeId,
Env: entry.Env,
InitScriptText: entry.ScriptText,
RpcContext: entry.RpcContext,
}, nil
}
func (impl *WshRouterControlImpl) AuthenticateTokenVerifyCommand(ctx context.Context, data wshrpc.CommandAuthenticateTokenData) (wshrpc.CommandAuthenticateRtnData, error) {
if !impl.Router.IsRootRouter() {
return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("authenticatetokenverify can only be called on root router")
}
if data.Token == "" {View on GitHub (pinned to a4447c1563)
Solutions
- Mint the connection token with a leaf (non-router) RpcContext: set IsRouter=false and supply a valid RouteId or ProcRoute.
- If the peer really is a router, use the JWT-based AuthenticateCommand flow instead of AuthenticateTokenCommand.
- Audit the token-minting code path that created the entry to confirm it targets leaf connections.
Example fix
// before
rpcCtx := &wshrpc.RpcContext{IsRouter: true}
token := shellutil.MintConnectionToken(rpcCtx, ...) // rejected at verify
// after
rpcCtx := &wshrpc.RpcContext{IsRouter: false, RouteId: "client-route-id"}
token := shellutil.MintConnectionToken(rpcCtx, ...) Defensive patterns
Strategy: validation
Validate before calling
// on the minting side, before creating the token
if rpcCtx.IsRouter {
return fmt.Errorf("use JWT AuthenticateCommand for router contexts, not token-swap")
} Prevention
- Only mint token-swap entries for leaf (non-router) RpcContexts
- Route router links through the JWT AuthenticateCommand flow
- Audit custom minting scripts for IsRouter: true
When it happens
Trigger: Calling AuthenticateTokenCommand / AuthenticateTokenVerifyCommand with a valid, unredeemed token whose stored RpcContext was created with IsRouter set — i.e. a token minted for a router entity rather than a leaf.
Common situations: Misconfigured tooling minting connection tokens for router contexts; a proxy/router node trying to bootstrap via token-swap instead of JWT auth; custom scripts copying the token minting code with IsRouter: true.
Related errors
- error setting up rpc client: %w
- error making jwt token: %w
- no token entry found
- no routeid
- authenticatetokenverify can only be called on root router
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/7c372aeb254bde80.
Report an issue: GitHub.