nats-io/nats-server · error

account not permitted as valid account option for auth…

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.

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.

Appendix: 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)