nats-io/nats-server · error

user JWT is not valid: %v

Error message

user JWT is not valid: %v

What it means

The user JWT's issuer was a known scoped signing key on the target account, but scope.ValidateScopedSigner(arc) failed: the claims do not satisfy the restrictions of that signing key's scope (e.g. disallowed permission templates, bearer token constraints, or other scoped-signer rules violated). The server rejects the user JWT as invalid.

Source

Thrown at server/auth_callout.go:237

			// 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()
			respCh <- titleCase(err.Error())
			return
		}
		// If the caller had established that the user should go through a proxy,

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Read the wrapped %v detail from ValidateScopedSigner and align the user claims with the scope's restrictions.
  2. Regenerate the signing key scope with nsc so the callout's emitted permissions fit the template.
  3. Sign with a non-scoped account signing key if the callout needs unrestricted claim shapes (and the account JWT permits it).
  4. Update the callout's claim construction to honor the UserScope template (see processUserPermissionsTemplate usage).

Example fix

// before
arc.User.UserPermissionLimits.Pub.Allow = []string{"{">", "all-subjects"} // outside scope template
// after
arc.User.UserPermissionLimits.Pub.Allow = []string{"service.subjects.only"} // within the scoped signer template
Defensive patterns

Strategy: validation

Validate before calling

scope, ok := targetAccScopes[arc.Issuer]
if ok {
    if err := scope.ValidateScopedSigner(arc); err != nil {
        return fmt.Errorf("claims violate scoped signer: %v", err)
    }
}

Try / catch

if err != nil && strings.Contains(err.Error(), "user JWT is not valid") {
    // read the wrapped ValidateScopedSigner detail and fix claim shape
}

Prevention

When it happens

Trigger: Operator-mode callout signs a user JWT with a scoped signing key whose UserScope requirements are not met by the emitted claims — ValidateScopedSigner returns a non-nil err which is wrapped here.

Common situations: Callout building permissions that violate the scope's template (e.g. adding pub/sub subjects outside the scoped template); using a scoped key while emitting claims incompatible with scope limits after an nsc/scopes change.

Related errors


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