nats-io/nats-server · error

user JWT issuer %q is not known

Error message

user JWT issuer %q is not known

What it means

In operator mode, the server verified that the signing key that emitted the user JWT (arc.Issuer) is a known issuer on the target account via targetAcc.hasIssuer. The issuer is neither the account's identity key nor one of its signing keys, so the user JWT is rejected. Scoped signing keys are then validated further (see the following error).

Source

Thrown at server/auth_callout.go:233

		// if we are not in operator mode, they can specify placement as a tag
		var placement string
		if !isOperatorMode {
			// only allow placement if we are not in operator mode
			placement = arc.Audience
		} else {
			placement = issuerAccount
		}

		targetAcc, err := s.LookupAccount(placement)
		if err != nil {
			return nil, fmt.Errorf("no valid account %q for auth callout response on account %q: %v", placement, account, err)
		}
		if isOperatorMode {
			// this will validate the signing key that emitted the user, and if it is a signing
			// key it assigns the permissions from the target account
			if scope, ok := targetAcc.hasIssuer(arc.Issuer); !ok {
				return nil, fmt.Errorf("user JWT issuer %q is not known", arc.Issuer)
			} else if scope != nil {
				// this possibly has to be different because it could just be a plain issued by a non-scoped signing key
				if err := scope.ValidateScopedSigner(arc); err != nil {
					return nil, fmt.Errorf("user JWT is not valid: %v", err)
				} else if uSc, ok := scope.(*jwt.UserScope); !ok {
					return nil, fmt.Errorf("user JWT is not a valid scoped user")
				} else if arc.User.UserPermissionLimits, err = processUserPermissionsTemplate(uSc.Template, arc, targetAcc); err != nil {
					return nil, fmt.Errorf("user JWT generated invalid permissions: %v", err)
				}
			}
		}
		return targetAcc, nil
	}

	processReply := func(_ *subscription, rc *client, racc *Account, subject, reply string, rmsg []byte) {
		arc, err := decodeResponse(rc, rmsg, racc)
		if err != nil {
			c.authViolation()

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Sign the user JWT with the target account's identity key or one of its configured signing keys.
  2. Add the key the callout uses to the target account JWT's signing keys list and re-push the account claim.
  3. Audit key rotation: reconfigure the callout after signing keys are rotated.

Example fix

// before
signed, err := arc.Encode(oldSigningKey) // removed from account signing keys
// after
signed, err := arc.Encode(currentSigningKey) // listed in target account's signing_keys
Defensive patterns

Strategy: validation

Validate before calling

// callout side: confirm the signing key is one of the target account's issuers
if !accountSigningKeys.Contains(arc.Issuer) {
    return fmt.Errorf("issuer %q is not a signing key of the target account", arc.Issuer)
}

Prevention

When it happens

Trigger: Operator-mode auth callout emits a user JWT signed by a key that is not registered as an issuer/signing key of the resolved target account, so hasIssuer(arc.Issuer) returns ok=false.

Common situations: Signing with a key from a different account; account JWT updated and an old signing key removed while the callout still uses it; callout misconfigured with the wrong private key.

Related errors


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