wavetermdev/waveterm · error

authenticatejobmanagerverify can only be called on root rout

Error message

authenticatejobmanagerverify can only be called on root router

What it means

AuthenticateJobManagerVerifyCommand is a root-only control RPC: only the root router holds the wstore job records needed to verify a job auth token. If the router receiving this call is not the root router, it immediately rejects the call with this error.

Source

Thrown at pkg/wshutil/wshrouter_controlimpl.go:219

		}
	}

	if rtnData.RpcContext == nil {
		return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("no rpccontext in token response")
	}
	if rtnData.RouteId == "" {
		return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("no routeid in token response")
	}
	log.Printf("wshrouter authenticate-token success linkid=%d routeid=%q", linkId, rtnData.RouteId)
	impl.Router.trustLink(linkId, LinkKind_Leaf)
	impl.Router.bindRoute(linkId, rtnData.RouteId, true)

	return rtnData, nil
}

func (impl *WshRouterControlImpl) AuthenticateJobManagerVerifyCommand(ctx context.Context, data wshrpc.CommandAuthenticateJobManagerData) error {
	if !impl.Router.IsRootRouter() {
		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")

View on GitHub (pinned to a4447c1563)

Solutions

  1. Ensure the RPC is routed to ControlRootRoute so it lands on the root router.
  2. Connect to the actual root wave process for job-manager verification instead of a relay.
  3. Check impl.Router.IsRootRouter() configuration of the instance you are calling; create/use the root router.
  4. Fix tests/harnesses to use a root router for this command.

Example fix

// before
err := wshRpc.SendRpcRequest(wshrpc.Command_AuthenticateJobManagerVerify, data, &wshrpc.RpcOpts{Route: someRelayRoute})
// after
err := wshRpc.SendRpcRequest(wshrpc.Command_AuthenticateJobManagerVerify, data, &wshrpc.RpcOpts{Route: wshutil.ControlRootRoute})
Defensive patterns

Strategy: validation

Validate before calling

// only send this command when connected to the root
if !conn.TargetIsRootRouter() {
    return fmt.Errorf("refusing: authenticatejobmanagerverify must go to the root router")
}

Try / catch

err := wshRpc.SendRpcRequest(wshrpc.Command_AuthenticateJobManagerVerify, data, &wshrpc.RpcOpts{Route: wshutil.ControlRootRoute})
if err != nil && strings.Contains(err.Error(), "can only be called on root router") {
    return fmt.Errorf("misrouted RPC: fix the RpcOpts.Route to %s", wshutil.ControlRootRoute)
}

Prevention

When it happens

Trigger: Routing Command_AuthenticateJobManagerVerify to a non-root (relay/leaf) router — e.g. wrong RpcOpts.Route, or calling the command locally on a middle-layer router instance instead of the root.

Common situations: Job manager configured to talk to a downstream wavelock/ssh relay instead of the root wave process; test harness instantiating a child router and issuing the verify command against it.

Understand the failure class

Related errors


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