nats-io/nats-server · error

not trusted

Error message

not trusted

What it means

In nats-server, when a resolver processes an account-delete operator claim, it verifies the claim is self-signed by the operator and that the operator's public key appears in the server's trusted keys map (store.operator). The 'not trusted' error is returned when the deleting key's issuer is not among the server's configured trusted operator keys, so the delete request is rejected as coming from an untrusted operator.

Source

Thrown at server/accounts.go:4373

	}
}

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
				}
			}
		}
	}
	if err != nil {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Sign the delete claim with a signing key listed in the server's trusted operator keys (TrustedOperators / resolver operator config).
  2. Add the operator public key that issued the claim to the server's operator config and reload/restart.
  3. Regenerate the claim with the current operator key (nsc) so subject == issuer and the issuer is trusted.

Example fix

// before
erp, _ := nkeys.FromSeed(oldOperatorSeed)
claim.JWT, _ = claim.Encode(erp) // signed with untrusted key
// after
erp, _ := nkeys.FromSeed(currentOperatorSeed) // key trusted by server config
claim.JWT, _ = claim.Encode(erp)
Defensive patterns

Strategy: validation

Validate before calling

pub, err := nkeys.FromPublicKey(operatorPubKey)
if err != nil || operatorPubKey != claim.Issuer {
    return fmt.Errorf("operator key %s not trusted by server", claim.Issuer)
}

Try / catch

if err := pushDeleteClaim(claim); err != nil {
    if strings.Contains(err.Error(), "not trusted") {
        log.Fatalf("claim issuer %q not in server trusted keys; re-sign with trusted operator key", claim.Issuer)
    }
}

Prevention

When it happens

Trigger: Calling the account-delete resolver flow with a delete claim whose Issuer (the operator signing key) is not present in the server's trusted operators / configured operator keys map. Also occurs when the server was configured with a different operator key than the one that signed the claim, or TrustedOperators changed after the claim was signed.

Common situations: Rotating operator signing keys without updating the server config; pointing a server at a different operator's resolver; using a dev key to sign claims for a production server; mixed-version clustered servers with different trusted key sets.

Related errors


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