@@ -420,10 +420,18 @@ final class LiveTailViewModel {
422 private func poll(client: ParseableClient, stream: String) async {
422 private func poll(client: ParseableClient, stream: String) async {
423+ let generation = pollGeneration
423 guard !isPolling else { return }
424 guard !isPolling else { return }
424 isPolling = true
425 isPolling = true
425− defer { isPolling = false }
426+ // Only release the guard if this poll is still the current generation. A
426− let generation = pollGeneration
427+ // stop()/start() during the network await can supersede this poll while a
428+ // newer-generation poll is already in flight; clearing isPolling
429+ // unconditionally would let a subsequent timer tick start a second poll
430+ // that overlaps the newer one, racing writes to lastTimestamp /
431+ // consecutiveErrors. start()/clear() reset isPolling on their own
432+ // generation bump, so a superseded poll leaving it set can't wedge the
434+ defer { if generation == pollGeneration { isPolling = false } }
428 let now = Date()
436 let now = Date()
429 // The Parseable server truncates query time-ranges to minute
437 // The Parseable server truncates query time-ranges to minute