wavetermdev/waveterm · error
failed to validate token: %w
Error message
failed to validate token: %w
What it means
During peer authentication, the incoming JobAccessToken presented by the connecting party is validated with wavejwt.ValidateAndExtract; if the JWT is invalid (bad signature, expired, malformed), this error wraps the JWT library error. The server rejects the connection before any job commands are allowed.
Source
Thrown at pkg/jobmanager/mainserverconn.go:75
JobAuthToken: jobAuthToken,
}
err := wshclient.AuthenticateJobManagerCommand(msc.WshRpc, authData, &wshrpc.RpcOpts{Route: wshutil.ControlRoute})
if err != nil {
log.Printf("authenticateSelfToServer: failed to authenticate to server: %v\n", err)
return fmt.Errorf("failed to authenticate to server: %w", err)
}
msc.SelfAuthenticated.Store(true)
log.Printf("authenticateSelfToServer: successfully authenticated to server\n")
return nil
}
func (msc *MainServerConn) AuthenticateToJobManagerCommand(ctx context.Context, data wshrpc.CommandAuthenticateToJobData) error {
jobId, jobAuthToken := WshCmdJobManager.GetJobAuthInfo()
claims, err := wavejwt.ValidateAndExtract(data.JobAccessToken)
if err != nil {
log.Printf("AuthenticateToJobManager: failed to validate token: %v\n", err)
return fmt.Errorf("failed to validate token: %w", err)
}
if !claims.MainServer {
log.Printf("AuthenticateToJobManager: MainServer claim not set\n")
return fmt.Errorf("MainServer claim not set")
}
if claims.JobId != jobId {
log.Printf("AuthenticateToJobManager: JobId mismatch: expected %s, got %s\n", jobId, claims.JobId)
return fmt.Errorf("JobId mismatch")
}
msc.PeerAuthenticated.Store(true)
log.Printf("AuthenticateToJobManager: authentication successful for JobId=%s\n", claims.JobId)
err = msc.authenticateSelfToServer(jobAuthToken)
if err != nil {
msc.PeerAuthenticated.Store(false)
return err
}
View on GitHub (pinned to a4447c1563)
Solutions
- Obtain a fresh JobAccessToken from the main server and retry the authentication.
- Verify both sides share the same JWT signing secret/key and that clocks are synced (NTP).
- Decode the wrapped error to see the JWT cause (expired vs signature) and fix accordingly.
Example fix
// before
claims, err := wavejwt.ValidateAndExtract(data.JobAccessToken)
// after
if claims, err := wavejwt.ValidateAndExtract(data.JobAccessToken); err != nil {
return fmt.Errorf("token invalid (%v); request a fresh JobAccessToken from the main server", err)
} else { _ = claims } Defensive patterns
Strategy: validation
Validate before calling
parts := strings.Split(data.JobAccessToken, ".")
if len(parts) != 3 {
return fmt.Errorf("malformed JobAccessToken: must be a 3-part JWT")
} Try / catch
if err := conn.AuthenticateToJobManagerCommand(ctx, authData); err != nil {
if strings.Contains(err.Error(), "failed to validate token") {
authData.JobAccessToken = fetchFreshToken()
err = conn.AuthenticateToJobManagerCommand(ctx, authData)
}
if err != nil { return err }
} Prevention
- Refresh tokens before expiry instead of caching indefinitely
- Sync clocks via NTP on all hosts
- Never hand-edit or truncate JWTs
When it happens
Trigger: AuthenticateToJobManagerCommand receives data.JobAccessToken that fails wavejwt.ValidateAndExtract — expired token, signed by the wrong key, or structurally malformed JWT.
Common situations: Stale access tokens after server key rotation, clock skew between machines causing premature expiry, copying an old token, or truncated tokens from manual copy/paste.
Related errors
- error authenticating with upstream: %v
- MainServer claim not set
- JobId mismatch
- error getting jwt public key: %v
- failed to authenticate to server: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/0c9aa765fc6756fd.
Report an issue: GitHub.