wavetermdev/waveterm · error
invalid jobauthtoken
Error message
invalid jobauthtoken
What it means
The job record was found, but the supplied JobAuthToken does not equal the job.JobAuthToken stored in the database. This is a deliberate authentication failure: the caller presented the wrong (or stale) secret for the job and the router refuses to trust the link for that job.
Source
Thrown at pkg/wshutil/wshrouter_controlimpl.go:237
return fmt.Errorf("authenticatejobmanagerverify can only be called on root router")
}
if data.JobId == "" {
return fmt.Errorf("no jobid in authenticatejobmanager message")
}
if data.JobAuthToken == "" {
return fmt.Errorf("no jobauthtoken in authenticatejobmanager message")
}
job, err := wstore.DBMustGet[*waveobj.Job](ctx, data.JobId)
if err != nil {
log.Printf("wshrouter authenticate-jobmanager-verify error jobid=%q: failed to get job: %v", data.JobId, err)
return fmt.Errorf("failed to get job: %w", err)
}
if job.JobAuthToken != data.JobAuthToken {
log.Printf("wshrouter authenticate-jobmanager-verify error jobid=%q: invalid jobauthtoken", data.JobId)
return fmt.Errorf("invalid jobauthtoken")
}
log.Printf("wshrouter authenticate-jobmanager-verify success jobid=%q", data.JobId)
return nil
}
func (impl *WshRouterControlImpl) AuthenticateJobManagerCommand(ctx context.Context, data wshrpc.CommandAuthenticateJobManagerData) error {
handler := GetRpcResponseHandlerFromContext(ctx)
if handler == nil {
return fmt.Errorf("no response handler in context")
}
linkId := handler.GetIngressLinkId()
if linkId == baseds.NoLinkId {
return fmt.Errorf("no ingress link found")
}
if data.JobId == "" {
return fmt.Errorf("no jobid in authenticatejobmanager message")View on GitHub (pinned to a4447c1563)
Solutions
- Re-fetch the current job record / token from the authoritative source (the root that owns the job) and retry with the fresh token.
- If the job was recreated, re-register with the new token instead of the cached one.
- Verify no config or copy step truncates/transforms the token string.
- Confirm the JobId and JobAuthToken come from the same job creation response.
Example fix
// before
_, err = jobClient.Verify(wshrpc.CommandAuthenticateJobManagerData{JobId: cachedJobId, JobAuthToken: cachedToken})
// after
job, err := jobClient.Get(cachedJobId)
if err != nil { return err }
_, err = jobClient.Verify(wshrpc.CommandAuthenticateJobManagerData{JobId: cachedJobId, JobAuthToken: job.JobAuthToken}) Defensive patterns
Strategy: validation
Validate before calling
// fetch the authoritative token instead of using a cached one
job, err := jobStore.Get(ctx, data.JobId)
if err != nil { return err }
if job.JobAuthToken != data.JobAuthToken {
data.JobAuthToken = job.JobAuthToken // refresh stale secret before verifying
} Try / catch
err := verifyJobManager(ctx, data)
if err != nil && strings.Contains(err.Error(), "invalid jobauthtoken") {
// do not retry blindly: refresh the token from the authoritative job record first
return refreshJobTokenAndRetry(ctx, data.JobId)
} Prevention
- Never cache job auth tokens across job re-creation cycles.
- Keep JobId and JobAuthToken from the same creation response.
- Treat this error as a security signal — log it but never fall back to unverified operation.
When it happens
Trigger: AuthenticateJobManagerVerify / AuthenticateJobManager called with a JobAuthToken that mismatches the stored job.JobAuthToken — stale token after job re-creation, token from a different job, or a client that cached an old secret.
Common situations: Job recreated (new random token) but the client still holds the previous token; multiple environments sharing job ids with different secrets; secret truncated or altered in transit/config.
Related errors
- error authenticating with upstream: %v
- failed to authenticate to server: %w
- failed to validate token: %w
- MainServer claim not set
- JobId mismatch
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/bd7a3bc35e01063c.
Report an issue: GitHub.