diff --git a/include/libcamera/internal/request.h b/include/libcamera/internal/request.h
index 8c92a27a95e5..981ad184fffa 100644
--- a/include/libcamera/internal/request.h
+++ b/include/libcamera/internal/request.h
@@ -59,6 +59,8 @@ private:
 	std::unordered_set<FrameBuffer *> pending_;
 	std::map<FrameBuffer *, std::unique_ptr<EventNotifier>> notifiers_;
 	std::unique_ptr<Timer> timer_;
+
+	uint32_t canary_;
 };
 
 } /* namespace libcamera */
diff --git a/include/libcamera/request.h b/include/libcamera/request.h
index dffde1536cad..8e377de14b12 100644
--- a/include/libcamera/request.h
+++ b/include/libcamera/request.h
@@ -65,6 +65,8 @@ public:
 
 	std::string toString() const;
 
+	bool canary() const;
+
 private:
 	LIBCAMERA_DISABLE_COPY(Request)
 
@@ -74,6 +76,8 @@ private:
 
 	const uint64_t cookie_;
 	Status status_;
+
+	int32_t canary_;
 };
 
 std::ostream &operator<<(std::ostream &out, const Request &r);
diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
index 48bf19b28979..3b72098cce59 100644
--- a/src/libcamera/camera.cpp
+++ b/src/libcamera/camera.cpp
@@ -1116,6 +1116,11 @@ int Camera::queueRequest(Request *request)
 {
 	Private *const d = _d();
 
+	if (request->canary()) {
+		LOG(Camera, Error) << "Invalid request";
+		return -EINVAL;
+	}
+
 	int ret = d->isAccessAllowed(Private::CameraRunning);
 	if (ret < 0)
 		return ret;
diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp
index 949c556fa437..cfe732765f86 100644
--- a/src/libcamera/request.cpp
+++ b/src/libcamera/request.cpp
@@ -23,6 +23,8 @@
 #include "libcamera/internal/framebuffer.h"
 #include "libcamera/internal/tracepoints.h"
 
+#define REQUEST_CANARY 0x1F2E3D4C
+
 /**
  * \file libcamera/request.h
  * \brief Describes a frame capture request to be processed by a camera
@@ -48,13 +50,14 @@ LOG_DEFINE_CATEGORY(Request)
  * \param camera The Camera that creates the request
  */
 Request::Private::Private(Camera *camera)
-	: camera_(camera), cancelled_(false)
+	: camera_(camera), cancelled_(false), canary_(REQUEST_CANARY)
 {
 }
 
 Request::Private::~Private()
 {
 	doCancelRequest();
+	canary_ = 0;
 }
 
 /**
@@ -114,6 +117,7 @@ void Request::Private::complete()
 {
 	Request *request = _o<Request>();
 
+	ASSERT(canary_ == REQUEST_CANARY);
 	ASSERT(request->status() == RequestPending);
 	ASSERT(!hasPendingBuffers());
 
@@ -128,6 +132,8 @@ void Request::Private::doCancelRequest()
 {
 	Request *request = _o<Request>();
 
+	ASSERT(canary_ == REQUEST_CANARY);
+
 	for (FrameBuffer *buffer : pending_) {
 		buffer->_d()->cancel();
 		camera_->bufferCompleted.emit(request, buffer);
@@ -149,6 +155,8 @@ void Request::Private::doCancelRequest()
  */
 void Request::Private::cancel()
 {
+	ASSERT(canary_ == REQUEST_CANARY);
+
 	LIBCAMERA_TRACEPOINT(request_cancel, this);
 
 	Request *request = _o<Request>();
@@ -346,7 +354,7 @@ void Request::Private::timeout()
  */
 Request::Request(Camera *camera, uint64_t cookie)
 	: Extensible(std::make_unique<Private>(camera)),
-	  cookie_(cookie), status_(RequestPending)
+	  cookie_(cookie), status_(RequestPending), canary_(REQUEST_CANARY)
 {
 	controls_ = new ControlList(controls::controls,
 				    camera->_d()->validator());
@@ -367,6 +375,8 @@ Request::~Request()
 
 	delete metadata_;
 	delete controls_;
+
+	canary_ = 0;
 }
 
 /**
@@ -381,6 +391,11 @@ Request::~Request()
  */
 void Request::reuse(ReuseFlag flags)
 {
+	if (canary_ != REQUEST_CANARY) {
+		LOG(Request, Error) << "Invalid Request object";
+		return;
+	}
+
 	LIBCAMERA_TRACEPOINT(request_reuse, this);
 
 	_d()->reset();
@@ -462,6 +477,11 @@ void Request::reuse(ReuseFlag flags)
 int Request::addBuffer(const Stream *stream, FrameBuffer *buffer,
 		       std::unique_ptr<Fence> fence)
 {
+	if (canary_ != REQUEST_CANARY) {
+		LOG(Request, Error) << "Attempt to add buffer to invalid request";
+		return -EINVAL;
+	}
+
 	if (!stream) {
 		LOG(Request, Error) << "Invalid stream reference";
 		return -EINVAL;
@@ -509,6 +529,11 @@ int Request::addBuffer(const Stream *stream, FrameBuffer *buffer,
  */
 FrameBuffer *Request::findBuffer(const Stream *stream) const
 {
+	if (canary_ != REQUEST_CANARY) {
+		LOG(Request, Error) << "Invalid Request object";
+		return nullptr;
+	}
+
 	const auto it = bufferMap_.find(stream);
 	if (it == bufferMap_.end())
 		return nullptr;
@@ -571,6 +596,7 @@ uint32_t Request::sequence() const
  */
 bool Request::hasPendingBuffers() const
 {
+	ASSERT(canary_ == REQUEST_CANARY);
 	return !_d()->pending_.empty();
 }
 
@@ -590,6 +616,25 @@ std::string Request::toString() const
 	return ss.str();
 }
 
+/**
+ * \brief Identify if the Request object is valid and alive
+ *
+ * This provides a means of checking if the request is a valid request object.
+ * While Requests are constructed by libcamera, they are owned and may be freed
+ * by applications. It can be all to easy too release a Request object while it
+ * is still in use by libcamera - or attempt to requeue invalid or deleted
+ * requests.
+ *
+ * The canary provides an insight that the object is not valid and shall be
+ * rejected by libcamera API calls.
+ *
+ * \return True if the canary has died, and the object shall not be trusted
+ */
+bool Request::canary() const
+{
+	return canary_ != REQUEST_CANARY;
+}
+
 /**
  * \brief Insert a text representation of a Request into an output stream
  * \param[in] out The output stream
@@ -601,6 +646,11 @@ std::ostream &operator<<(std::ostream &out, const Request &r)
 	/* Pending, Completed, Cancelled(X). */
 	static const char *statuses = "PCX";
 
+	if (r.canary()) {
+		out << "Request(Invalid Canary)";
+		return out;
+	}
+
 	/* Example Output: Request(55:P:1/2:6523524) */
 	out << "Request(" << r.sequence() << ":" << statuses[r.status()] << ":"
 	    << r._d()->pending_.size() << "/" << r.buffers().size() << ":"
