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

  1. Re-fetch the current job record / token from the authoritative source (the root that owns the job) and retry with the fresh token.
  2. If the job was recreated, re-register with the new token instead of the cached one.
  3. Verify no config or copy step truncates/transforms the token string.
  4. 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

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


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/bd7a3bc35e01063c. Report an issue: GitHub.