diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
index d91886bed9..bbe8e06d40 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
@@ -90,6 +90,18 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
 
     LOG_DEBUG(Service_Nvnflinger, "acquiring slot={}", slot);
 
+    // If the front buffer is still being tracked, update its slot state
+    if (core->StillTracking(*front)) {
+        slots[slot].acquire_called = true;
+        slots[slot].needs_cleanup_on_release = false;
+        slots[slot].buffer_state = BufferState::Acquired;
+
+        // TODO: for now, avoid resetting the fence, so that when we next return this
+        // slot to the producer, it will wait for the fence to pass. We should fix this
+        // by properly waiting for the fence in the BufferItemConsumer.
+        // slots[slot].fence = Fence::NoFence();
+    }
+
     // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
     // avoid unnecessarily remapping this buffer on the consumer side.
     if (out_buffer->acquire_called) {
@@ -132,11 +144,28 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
             ++current;
         }
 
-        slots[slot].buffer_state = BufferState::Free;
+        if (slots[slot].buffer_state == BufferState::Acquired) {
+            // TODO: for now, avoid resetting the fence, so that when we next return this
+            // slot to the producer, it can wait for its own fence to pass. We should fix this
+            // by properly waiting for the fence in the BufferItemConsumer.
+            // slots[slot].fence = release_fence;
+            slots[slot].buffer_state = BufferState::Free;
 
-        listener = core->connected_producer_listener;
+            listener = core->connected_producer_listener;
 
-        LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot);
+            LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot);
+        } else if (slots[slot].needs_cleanup_on_release) {
+            LOG_DEBUG(Service_Nvnflinger, "releasing a stale buffer slot {} (state = {})", slot,
+                      slots[slot].buffer_state);
+            slots[slot].needs_cleanup_on_release = false;
+            return Status::StaleBufferSlot;
+        } else {
+            LOG_ERROR(Service_Nvnflinger,
+                      "attempted to release buffer slot {} but its state was {}", slot,
+                      slots[slot].buffer_state);
+
+            return Status::BadValue;
+        }
 
         core->SignalDequeueCondition();
     }
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
index 4ed5e59782..5d8c861fa7 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
@@ -74,6 +74,10 @@ void BufferQueueCore::FreeBufferLocked(s32 slot) {
 
     slots[slot].graphic_buffer.reset();
 
+    if (slots[slot].buffer_state == BufferState::Acquired) {
+        slots[slot].needs_cleanup_on_release = true;
+    }
+
     slots[slot].buffer_state = BufferState::Free;
     slots[slot].frame_number = UINT32_MAX;
     slots[slot].acquire_called = false;
diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h
index d25bca049d..37daca78b1 100644
--- a/src/core/hle/service/nvnflinger/buffer_slot.h
+++ b/src/core/hle/service/nvnflinger/buffer_slot.h
@@ -31,6 +31,7 @@ struct BufferSlot final {
     u64 frame_number{};
     Fence fence;
     bool acquire_called{};
+    bool needs_cleanup_on_release{};
     bool attached_by_consumer{};
     bool is_preallocated{};
 };