Repository: nats-io/nats-server

Issue:
cluster_traffic option cannot be set in account jetstream ssection
### Observed behavior

Server fails to start  with valid config.
Also the error messages are inconsistent ("owner" versus "account")

```
accounts {
  A {
     jetstreams {
       cluster_traffic: "owner",
       ...
```

nats-server: config\nats02b_jetstream.config:19:9: runtime error: invalid memory address or nil pointer dereference

### Expected behavior

Server should start with the configuration below

### Server and client version

2.11 and main branch (26.3.2025)

### Host environment

Windows 11

### Steps to reproduce

Start server with the configuration below

```
host: 0.0.0.0
port: 4222;

jetstream {
	store_dir: js/jsdata02
    max_file: 500M
    
    limits {
        max_ack_pending: 100,
        max_ha_assets: 2,
        max_request_batch: 100,
        duplicate_window: 120s
    }
}

accounts: {

    A: {
        jetstream {
            max_memory: 100M,
            max_file: 100M,
            max_streams: 2,
            max_consumers 2,
            max_ack_pending: 200,
            memory_max_stream_bytes: 10M,
            store_max_stream_bytes: 10M,
            cluster_traffic: "system",
            max_bytes_required: false,
        }
        users: [
            {user: a, password: a}
           ]
    }
   

    SYS: { 
        users: [
            {user: admin, password: admin}
           ]
    },
}
system_account: SYS,    
no_auth_user: a
```

Discussion:
@borjaetxebarria: @neilalexander  is this really fixed?  I think the traffic still goes to the sys account (even when owner selected)

Solution patch:
diff --git a/server/accounts.go b/server/accounts.go
index 3c5bed95..44afc237 100644
--- a/server/accounts.go
+++ b/server/accounts.go
@@ -83,6 +83,7 @@ type Account struct {
 	exports      exportMap
 	js           *jsAccount
 	jsLimits     map[string]JetStreamAccountLimits
+	nrgAccount   string
 	limits
 	expired      atomic.Bool
 	incomplete   bool
@@ -3784,16 +3785,16 @@ func (s *Server) updateAccountClaimsWithRefresh(a *Account, ac *jwt.AccountClaim
 		// Check whether the account NRG status changed. If it has then we need to notify the
 		// Raft groups running on the system so that they can move their subs if needed.
 		a.mu.Lock()
-		previous := ajs.nrgAccount
+		previous := a.nrgAccount
 		switch ac.ClusterTraffic {
 		case "system", _EMPTY_:
-			ajs.nrgAccount = _EMPTY_
+			a.nrgAccount = _EMPTY_
 		case "owner":
-			ajs.nrgAccount = a.Name
+			a.nrgAccount = a.Name
 		default:
 			s.Errorf("Account claim for %q has invalid value %q for cluster traffic account", a.Name, ac.ClusterTraffic)
 		}
-		changed := ajs.nrgAccount != previous
+		changed := a.nrgAccount != previous
 		a.mu.Unlock()
 		if changed {
 			s.updateNRGAccountStatus()
diff --git a/server/jetstream.go b/server/jetstream.go
index 2ae974d6..7c68a458 100644
--- a/server/jetstream.go
+++ b/server/jetstream.go
@@ -176,9 +176,6 @@ type jsAccount struct {
 	updatesSub *subscription
 	lupdate    time.Time
 	utimer     *time.Timer
-
-	// Which account to send NRG traffic into. Empty string is system account.
-	nrgAccount string
 }
 
 // Track general usage for this account.
diff --git a/server/opts.go b/server/opts.go
index d36adaff..c8aab21e 100644
--- a/server/opts.go
+++ b/server/opts.go
@@ -2193,9 +2193,9 @@ func parseJetStreamForAccount(v any, acc *Account, errors *[]error) error {
 				}
 				switch vv {
 				case "system", _EMPTY_:
-					acc.js.nrgAccount = _EMPTY_
+					acc.nrgAccount = _EMPTY_
 				case "owner":
-					acc.js.nrgAccount = acc.Name
+					acc.nrgAccount = acc.Name
 				default:
 					return &configErr{tk, fmt.Sprintf("Expected 'system' or 'owner' string value for %q, got %v", mk, mv)}
 				}
diff --git a/server/raft.go b/server/raft.go
index 80902518..830b2d39 100644
--- a/server/raft.go
+++ b/server/raft.go
@@ -601,7 +601,7 @@ func (n *raft) recreateInternalSubsLocked() error {
 		if a, _ := n.s.lookupAccount(n.accName); a != nil {
 			a.mu.RLock()
 			if a.js != nil {
-				target = a.js.nrgAccount
+				target = a.nrgAccount
 			}
 			a.mu.RUnlock()
 		}



Test patch:
diff --git a/server/jetstream_cluster_4_test.go b/server/jetstream_cluster_4_test.go
index 774e9dc7..b4c2ad81 100644
--- a/server/jetstream_cluster_4_test.go
+++ b/server/jetstream_cluster_4_test.go
@@ -2011,7 +2011,7 @@ func TestJetStreamClusterAccountNRG(t *testing.T) {
 		}
 		time.Sleep(time.Millisecond * 100)
 		for _, s := range c.servers {
-			s.GlobalAccount().js.nrgAccount = ""
+			s.GlobalAccount().nrgAccount = ""
 			s.updateNRGAccountStatus()
 		}
 
@@ -2051,9 +2051,9 @@ func TestJetStreamClusterAccountNRG(t *testing.T) {
 		time.Sleep(time.Millisecond * 100)
 		for i, s := range c.servers {
 			if i == 0 {
-				s.GlobalAccount().js.nrgAccount = globalAccountName
+				s.GlobalAccount().nrgAccount = globalAccountName
 			} else {
-				s.GlobalAccount().js.nrgAccount = ""
+				s.GlobalAccount().nrgAccount = ""
 			}
 			s.updateNRGAccountStatus()
 		}
@@ -2092,7 +2092,7 @@ func TestJetStreamClusterAccountNRG(t *testing.T) {
 		}
 		time.Sleep(time.Millisecond * 100)
 		for _, s := range c.servers {
-			s.GlobalAccount().js.nrgAccount = globalAccountName
+			s.GlobalAccount().nrgAccount = globalAccountName
 			s.updateNRGAccountStatus()
 		}
 
@@ -2123,6 +2123,34 @@ func TestJetStreamClusterAccountNRG(t *testing.T) {
 	})
 }
 
+func TestJetStreamClusterAccountNRGConfigNoPanic(t *testing.T) {
+	clusterConf := `
+		listen: 127.0.0.1:-1
+
+		server_name: %s
+		jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
+
+		cluster {
+			name: %s
+			listen: 127.0.0.1:%d
+			routes = [%s]
+		}
+
+		accounts {
+			ONE { jetstream: { cluster_traffic: system } }
+		}
+	`
+
+	cl := createJetStreamClusterWithTemplate(t, clusterConf, "test", 3)
+	defer cl.shutdown()
+
+	for _, s := range cl.servers {
+		acc, err := s.lookupAccount("ONE")
+		require_NoError(t, err)
+		require_Equal(t, acc.nrgAccount, _EMPTY_) // Empty for the system account
+	}
+}
+
 func TestJetStreamClusterWQRoundRobinSubjectRetention(t *testing.T) {
 	c := createJetStreamClusterExplicit(t, "R3S", 3)
 	defer c.shutdown()
diff --git a/server/jetstream_jwt_test.go b/server/jetstream_jwt_test.go
index 209d5c92..580b4054 100644
--- a/server/jetstream_jwt_test.go
+++ b/server/jetstream_jwt_test.go
@@ -1643,11 +1643,11 @@ func TestJetStreamJWTClusterAccountNRG(t *testing.T) {
 			require_True(t, acc.js != nil)
 			switch state {
 			case "system":
-				require_Equal(t, acc.js.nrgAccount, _EMPTY_)
+				require_Equal(t, acc.nrgAccount, _EMPTY_)
 			case "owner":
-				require_Equal(t, acc.js.nrgAccount, aExpPub)
+				require_Equal(t, acc.nrgAccount, aExpPub)
 			case thirdAcc:
-				require_Equal(t, acc.js.nrgAccount, aExpPub2)
+				require_Equal(t, acc.nrgAccount, aExpPub2)
 			}
 
 			// Now get a list of all of the Raft nodes that should