@@ -157,7 +157,11 @@ struct StreamDetailView: View {
157157 }
158158 .padding()
159159 }
160− .task(id: stream) {
160+ // Key on the connection too: switching to a different server that
161+ // exposes a same-named stream leaves `stream` unchanged, so without
162+ // the connection id the task wouldn't refire and the old server's
163+ // data would linger.
164+ .task(id: "\(appState.activeConnection?.id.uuidString ?? "")|\(stream)") {
161165 await loadData(stream: stream)
162166 }
163167 } else {
@@ -172,6 +176,11 @@ struct StreamDetailView: View {
172176
173177 private func loadData(stream: String) async {
174178 guard let client = appState.client else { return }
179+ // Capture the connection this fetch belongs to. Two servers can expose a
180+ // same-named stream, so guarding on the stream name alone would let a
181+ // slow fetch write the previous server's data under the new server's
182+ // stream after a server switch.
183+ let connID = appState.activeConnection?.id
175184 isLoading = true
176185 errorMessage = nil
177186 // Clear previous stream's data so it isn't shown under the new header
@@ -205,8 +214,13 @@ struct StreamDetailView: View {
205214 // The manual Refresh/Retry buttons spawn detached Tasks that aren't tied to
206215 // `.task(id: stream)` cancellation, so a slow refresh for the old stream can
207216 // resolve after the user switched. Drop stale results instead of writing
208− // them under the new stream's header.
209− guard stream == appState.selectedStream else { return }
217+ // them under the new stream's header. Also check the connection so a
218+ // same-named stream on a different server doesn't accept the old server's
219+ // data. Reset `isLoading` on the stale path or the spinner wedges forever.
220+ guard stream == appState.selectedStream, connID == appState.activeConnection?.id else {
221+ isLoading = false
222+ return
223+ }
210224
211225 schema = newSchema
212226 stats = newStats