diff --git a/components/rgbd-sources/include/ftl/rgbd/source.hpp b/components/rgbd-sources/include/ftl/rgbd/source.hpp
index 55cb8554e9eded1b661a89cdf917025369467ad2..f5e27cd882fe00c3c25dd97bcb9dbc69ea450d71 100644
--- a/components/rgbd-sources/include/ftl/rgbd/source.hpp
+++ b/components/rgbd-sources/include/ftl/rgbd/source.hpp
@@ -199,7 +199,8 @@ class Source : public ftl::Configurable {
 	SHARED_MUTEX &mutex() { return mutex_; }
 
 	std::function<void(int64_t, const cv::Mat &, const cv::Mat &)> &callback() { return callback_; }
-	void setCallback(std::function<void(int64_t, const cv::Mat &, const cv::Mat &)> cb) { callback_ = cb; }
+	void setCallback(std::function<void(int64_t, const cv::Mat &, const cv::Mat &)> cb);
+	void removeCallback() { callback_ = nullptr; }
 
 
 	private:
diff --git a/components/rgbd-sources/src/group.cpp b/components/rgbd-sources/src/group.cpp
index 88b9afcdca48fd141ca6487637dc7c78f9181d87..b8dbd6369376cb54f28bb912729a02d48d24dbc2 100644
--- a/components/rgbd-sources/src/group.cpp
+++ b/components/rgbd-sources/src/group.cpp
@@ -11,7 +11,9 @@ Group::Group() : framesets_(kFrameBufferSize), head_(0) {
 }
 
 Group::~Group() {
-
+	for (auto s : sources_) {
+		s->removeCallback();
+	}
 }
 
 void Group::addSource(ftl::rgbd::Source *src) {
diff --git a/components/rgbd-sources/src/source.cpp b/components/rgbd-sources/src/source.cpp
index c5efd26fc51068b74cfdc03e84cc3800a5931ea8..17e343a00b9523404f8a5b48ff85d15c0a0d9f9a 100644
--- a/components/rgbd-sources/src/source.cpp
+++ b/components/rgbd-sources/src/source.cpp
@@ -355,3 +355,8 @@ bool Source::setChannel(ftl::rgbd::channel_t c) {
 const ftl::rgbd::Camera Source::parameters(ftl::rgbd::channel_t chan) const {
 	return (impl_) ? impl_->parameters(chan) : parameters();
 }
+
+void Source::setCallback(std::function<void(int64_t, const cv::Mat &, const cv::Mat &)> cb) {
+	if (bool(callback_)) LOG(ERROR) << "Source already has a callback: " << getURI();
+	callback_ = cb;
+}