diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
index e1a74d89..2c5af05b 100644
--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
@@ -732,7 +732,9 @@ int PipelineHandlerRPi::queueRequestDevice(Camera *camera, Request *request)
 	for (auto stream : data->streams_) {
 		if (stream->isExternal()) {
 			FrameBuffer *buffer = request->findBuffer(stream);
-			stream->queueBuffer(data->dropFrameCount_ ? nullptr : buffer);
+			int ret = stream->queueBuffer(data->dropFrameCount_ ? nullptr : buffer);
+			if (ret)
+				return ret;
 		}
 	}
 
diff --git a/src/libcamera/pipeline/raspberrypi/rpi_stream.h b/src/libcamera/pipeline/raspberrypi/rpi_stream.h
index a6f573fa..3309edfb 100644
--- a/src/libcamera/pipeline/raspberrypi/rpi_stream.h
+++ b/src/libcamera/pipeline/raspberrypi/rpi_stream.h
@@ -139,28 +139,77 @@ public:
 			if (availableBuffers_.empty()) {
 				LOG(RPISTREAM, Warning) << "No buffers available for "
 							<< name_;
-				return -EINVAL;
+				/*
+				 * Note that we need to requeue an internal buffer as soon
+				 * as one becomes available.
+				 */
+				requeueBuffers_.push(nullptr);
+				return -ENOMEM;
 			}
 
 			buffer = availableBuffers_.front();
 			availableBuffers_.pop();
 		}
 
-		LOG(RPISTREAM, Debug) << "Queuing buffer " << buffer->cookie()
-				      << " for " << name_;
-
-		int ret = dev_->queueBuffer(buffer);
-		if (ret) {
-			LOG(RPISTREAM, Error) << "Failed to queue buffer for "
-					      << name_;
+		if (requeueBuffers_.empty()) {
+			/*
+			* No earlier requests are pending to be queued, so we can
+			* go ahead and queue the buffer into the device.
+			*/
+			LOG(RPISTREAM, Debug) << "Queuing buffer " << buffer->cookie()
+					      << " for " << name_;
+
+			int ret = dev_->queueBuffer(buffer);
+			if (ret)
+				LOG(RPISTREAM, Error) << "Failed to queue buffer for "
+						      << name_;
+			return ret;
+		} else {
+			/*
+			 * There are earlier buffers to be queued, so this buffer
+			 * must go on the waiting list.
+			 */
+			requeueBuffers_.push(buffer);
+			return 0;
 		}
-
-		return ret;
 	}
 
 	void returnBuffer(FrameBuffer *buffer)
 	{
+		/* Push this buffer back into the queue to be used again. */
 		availableBuffers_.push(buffer);
+
+		/*
+		 * Do we have any buffers that are waiting to be queued?
+		 * If so, do it now as availableBuffers_ will not be empty.
+		 */
+		while (!requeueBuffers_.empty()) {
+			FrameBuffer *buffer = requeueBuffers_.front();
+			requeueBuffers_.pop();
+
+			if (!buffer && !availableBuffers_.empty()) {
+				/*
+				 * We want to queue an internal buffer, and at
+				 * least one is available.
+				 */
+				buffer = availableBuffers_.front();
+				availableBuffers_.pop();
+			} else if (!buffer && !availableBuffers_.empty()) {
+				/*
+				 * We want to queue an internal buffer, but none
+				 * are available.
+				 */
+				break;
+			}
+
+			LOG(RPISTREAM, Debug) << "Queuing buffer " << buffer->cookie()
+					      << " for " << name_ << " from returnBuffer";
+
+			int ret = dev_->queueBuffer(buffer);
+			if (ret)
+				LOG(RPISTREAM, Error) << "Failed to queue buffer for "
+						      << name_ << " from returnBuffer";
+		}
 	}
 
 	bool findFrameBuffer(FrameBuffer *buffer) const
@@ -178,6 +227,7 @@ private:
 	void clearBuffers()
 	{
 		availableBuffers_ = std::queue<FrameBuffer *>{};
+		requeueBuffers_ = std::queue<FrameBuffer *>{};
 		internalBuffers_.clear();
 		bufferList_.clear();
 	}
@@ -193,7 +243,7 @@ private:
 	std::string name_;
 	/* The actual device stream. */
 	std::unique_ptr<V4L2VideoDevice> dev_;
-	/* All framebuffers associated with this device stream. */
+	/* All frame buffers associated with this device stream. */
 	std::vector<FrameBuffer *> bufferList_;
 	/*
 	 * List of frame buffer that we can use if none have been provided by
@@ -201,6 +251,15 @@ private:
 	 * buffers exported internally.
 	 */
 	std::queue<FrameBuffer *> availableBuffers_;
+	/*
+	 * List of frame buffer that are to be re-queued into the device.
+	 * A nullptr indicates any internal buffer can be used (from availableBuffers_),
+	 * whereas a valid pointer indicates an external buffer to be queued.
+	 *
+	 * Ordering buffers to be queued is important here as it must match the
+	 * requests coming from the application.
+	 */
+	std::queue<FrameBuffer *> requeueBuffers_;
 	/*
 	 * This is a list of buffers exported internally. Need to keep this around
 	 * as the stream needs to maintain ownership of these buffers.
