nats-io/nats-server · error

account %q not permitted as valid account option for auth ca

Error message

account %q not permitted as valid account option for auth callout for account %q

What it means

In operator mode, when the user JWT's issuer differs from the expected issuer, the server checks that the issuing account is listed in the target account's allowed_accounts (isAllowedAcount). If not, the callout is not permitted to issue users for that account and the response is rejected.

Source

Thrown at server/auth_callout.go:184

		}

		// 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 {
			c.Errorf("Outside connect times")
			return 0, nil, fmt.Errorf("authorized user on account %q outside of valid connect times", account)
		}

		allowedConnTypes, err := convertAllowedConnectionTypes(arc.User.AllowedConnectionTypes)
		if err != nil {
			c.Debugf("%v", err)
			if len(allowedConnTypes) == 0 {
				return 0, nil, fmt.Errorf("authorized user on account %q using invalid connection type", account)

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Add the issuing account's public key to the target account JWT's allowed_accounts list and re-push the account claim.
  2. Verify which account signed the user JWT and that it matches an entry in allowed_accounts.
  3. Ensure the updated account JWT is pushed to the resolver/JS account store so the server reloads it.

Example fix

// before (account JWT)
{ "allowed_accounts": [] }
// after
{ "allowed_accounts": ["ADZJPGQFYly2...issuing-account-pub..."] }
Defensive patterns

Strategy: validation

Validate before calling

// before connecting users, confirm the issuing account is permitted:
// decode target account JWT and check its allowed_accounts contains the issuer account public key

Prevention

When it happens

Trigger: Operator-mode deployment; the callout emits a user JWT signed by account X (cr.IssuerAccount/Issuer resolving to X), but X is not in the target account's allowed_accounts list, so acc.isAllowedAcount(jwtIssuer) fails.

Common situations: Adding a new callout/issuing account without adding it to allowed_accounts in the target account JWT; pushing updated account JWTs that dropped an allowed account entry.

Related errors


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