nats-io/nats-server · error

error non operator mode account %q: attempted to use issuer_

Error message

error non operator mode account %q: attempted to use issuer_account

What it means

In non-operator mode, the auth callout response's user JWT set an IssuerAccount, which is illegal: it would let the callout effectively issue users on a different account. The server rejects the response outright. IssuerAccount is only meaningful in operator (decentralized JWT) mode.

Source

Thrown at server/auth_callout.go:175

	// only in operator mode we expect to receive `issuer_account`.
	getIssuerAccount := func(arc *jwt.UserClaims, account string) (string, error) {
		// Make sure correct issuer.
		var issuer string
		if opts.AuthCallout != nil {
			issuer = opts.AuthCallout.Issuer
		} else {
			// Operator mode is who we send the request on unless switching accounts.
			issuer = acc.Name
		}

		// the jwt issuer can be a signing key
		jwtIssuer := arc.Issuer
		if arc.IssuerAccount != _EMPTY_ {
			if !isOperatorMode {
				// this should be invalid - effectively it would allow the auth callout
				// to issue on another account which may be allowed given the configuration
				// where the auth callout account can handle multiple different ones..
				return _EMPTY_, fmt.Errorf("error non operator mode account %q: attempted to use issuer_account", account)
			}
			jwtIssuer = arc.IssuerAccount
		}

		if jwtIssuer != issuer {
			if !isOperatorMode {
				return _EMPTY_, fmt.Errorf("wrong issuer for auth callout response on account %q, expected %q got %q", account, issuer, jwtIssuer)
			} else if !acc.isAllowedAcount(jwtIssuer) {
				return _EMPTY_, fmt.Errorf("account %q not permitted as valid account option for auth callout for account %q",
					arc.Issuer, account)
			}
		}
		return jwtIssuer, nil
	}

	getExpirationAndAllowedConnections := func(arc *jwt.UserClaims, account string) (time.Duration, map[string]struct{}, error) {
		allowNow, expiration := validateTimes(arc)
		if !allowNow {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Remove IssuerAccount from the claims the callout emits; rely on Issuer only.
  2. If per-account issuing is needed, run the deployment in operator mode with a proper account JWT.
  3. Verify the server's mode (operator vs local) matches what the callout service assumes.

Example fix

// before
arc.IssuerAccount = calloutAccountPub
// after
arc.IssuerAccount = _EMPTY_ // not allowed in non-operator mode
arc.Issuer = calloutAccountPub
Defensive patterns

Strategy: validation

Validate before calling

if arc.IssuerAccount != "" && !operatorMode {
    return errors.New("issuer_account not permitted in non-operator mode")
}

Prevention

When it happens

Trigger: A callout service builds AuthorizationResponseClaims/UserClaims with IssuerAccount populated while the server runs without an operator (account/server-local auth callout), then the user connects.

Common situations: Reusing callout code written for operator-mode deployments in a plain server setup; blindly copying example configs that set IssuerAccount.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/e727436e56353c3e. Report an issue: GitHub.