nats-io/nats-server · error

malformed request

Error message

malformed request

What it means

When validating an account-delete operator claim, the resolver requires a 'Data["accounts"]' field listing the account public keys to delete. If that key is absent from the claim's Data map, the request is rejected as 'malformed request'.

Source

Thrown at server/accounts.go:4375

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 {
		respondToUpdate(s, reply, _EMPTY_, fmt.Sprintf("delete accounts request by %s failed", subj), err)
		return

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Include an 'accounts' field (array of account public keys) in the delete claim's Data map.
  2. Regenerate the claim with nsc or the official jwt library so all required fields are populated.
  3. Log/inspect the claim JWT contents (decode it) to confirm Data.accounts exists before publishing.

Example fix

// before
data := map[string]any{} // missing accounts
// after
data := map[string]any{"accounts": []string{"AD..."}}
Defensive patterns

Strategy: validation

Validate before calling

raw, _ := json.Marshal(data)
var m map[string]any
json.Unmarshal(raw, &m)
if _, ok := m["accounts"]; !ok {
    return fmt.Errorf("delete claim must include Data.accounts")
}

Type guard

func hasAccountsList(d map[string]any) ([]any, bool) {
    list, ok := d["accounts"].([]any)
    return list, ok
}

Try / catch

if err := pushClaim(jwt); err != nil && strings.Contains(err.Error(), "malformed request") {
    decoded, _ := jwt.DecodeClaim()
    log.Fatalf("claim rejected: inspect Data map: %+v", decoded)
}

Prevention

When it happens

Trigger: Submitting a delete claim whose Data map has no 'accounts' entry — e.g. a claim constructed by hand or by tooling that omits the accounts list.

Common situations: Hand-crafted JSON claim payloads; custom automation calling the $JWS.U > account delete subject; nsc version mismatch producing different claim shapes; copy-pasted claim templates missing fields.

Understand the failure class

Related errors


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