nats-io/nats-server · error

not self signed

Error message

not self signed

What it means

Delete requests on the account resolver must be signed by the account itself (subject == issuer). If the decoded delete JWT's Subject differs from its Issuer — e.g. it was signed by an operator or signing key rather than the account key — the server rejects it with 'not self signed'.

Source

Thrown at server/accounts.go:4371

		response := map[string]any{"server": server, "data": accIds}
		s.sendInternalMsgLocked(reply, _EMPTY_, server, response)
	}
}

func handleDeleteRequest(store *DirJWTStore, s *Server, msg []byte, reply string) {
	var accIds []any
	var subj, sysAccName string
	if sysAcc := s.SystemAccount(); sysAcc != nil {
		sysAccName = sysAcc.GetName()
	}
	// Only operator and operator signing key are allowed to delete
	gk, err := jwt.DecodeGeneric(string(msg))
	if err == nil {
		subj = gk.Subject
		if store.deleteType == NoDelete {
			err = fmt.Errorf("delete must be enabled in server config")
		} else if subj != gk.Issuer {
			err = fmt.Errorf("not self signed")
		} else if _, ok := store.operator[gk.Issuer]; !ok {
			err = fmt.Errorf("not trusted")
		} else if list, ok := gk.Data["accounts"]; !ok {
			err = fmt.Errorf("malformed request")
		} else if accIds, ok = list.([]any); !ok {
			err = fmt.Errorf("malformed request")
		} else {
			for _, entry := range accIds {
				if acc, ok := entry.(string); !ok ||
					acc == _EMPTY_ || !nkeys.IsValidPublicAccountKey(acc) {
					err = fmt.Errorf("malformed request")
					break
				} else if acc == sysAccName {
					err = fmt.Errorf("not allowed to delete system account")
					break
				}
			}
		}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Sign the delete request with the account's own key pair so Issuer equals Subject
  2. Check nsc/operator setup so the delete is issued from the account context, not the operator
  3. Verify the JWT (decode and compare subject/issuer) before publishing the delete request

Example fix

// before
nsc delete account --sign-with-operator-key ... // operator-signed, rejected
// after
nsc delete account my-account // signed by the account key itself (issuer == subject)
Defensive patterns

Strategy: validation

Validate before calling

gk, err := jwt.DecodeGeneric(deleteJWT)
if err != nil {
    return err
}
if gk.Subject != gk.Issuer {
    return fmt.Errorf("delete request must be self-signed (subject %q != issuer %q)", gk.Subject, gk.Issuer)
}

Try / catch

if err := sendDeleteRequest(store, msg); err != nil {
    if strings.Contains(err.Error(), "not self signed") {
        // re-sign with the account key and resend
    }
}

Prevention

When it happens

Trigger: Publishing a delete request JWT whose issuer is an operator or operator signing key while the subject names an account, so gk.Subject != gk.Issuer; using the wrong nsc/operator context that signs with the operator key.

Common situations: Automation signing delete requests with operator credentials; using an operator-signed delete where account-signed is required; misconfigured nsc signing keys.

Related errors


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