diff --git a/components/structures/include/ftl/data/new_frame.hpp b/components/structures/include/ftl/data/new_frame.hpp
index 3eb1af3ee148b24f53e65318c5baafb0c6773879..9fa92ed140776c7cf2683c3b484dce61f21a5921 100644
--- a/components/structures/include/ftl/data/new_frame.hpp
+++ b/components/structures/include/ftl/data/new_frame.hpp
@@ -132,10 +132,10 @@ struct Aggregator {
  * generates discrete blocks of data with a timestamp, these blocks are
  * encapsulated in a frame that has any number of channels. A `Frame` must be
  * constructed from a `Pool` object so that memory can be reused.
- * 
+ *
  * It can be moved around but not copied since the quantity of data involved in
  * a frame is huge.
- * 
+ *
  * A frame goes through the following stages:
  *   1) Creation from reused memory in `Pool`
  *   2) Populate with incoming initial data/changes (from stream)
@@ -143,28 +143,28 @@ struct Aggregator {
  *   4) Create any new data such as new video frames
  *   5) Flush the data to transmit or save, becomes readonly
  *   6) Release memory to `Pool`
- * 
+ *
  * A channel stores one particular element of data of a specified type. To write
  * to a channel the `create` or `set` methods must be used, this will mark the
  * channel as changed but can only occur before the frame is flushed and
  * readonly. A `get` method allows const access to the data as long as the
  * channel exists.
- * 
+ *
  * On change events are triggered when `store` occurs, whereas on flush events
  * occur after flush. Both of these may occur on destruction if the frame was
  * not stored or flushed before destruction.
- * 
+ *
  * Some channels may fail `hasChannel` but still be marked as `available`. This
  * will be due to the data not being transmitted or encoded until requested.
- * 
+ *
  * Each frame is also associated with a `Session` object which stores all
  * persistent data. Persistent data can then be accessed via any `Frame` with
  * the same ID since they share a `Session`.
- * 
+ *
  * A `Frame` provides some basic methods, however, it can be cast to other
  * frame types using the cast method which provides additional wrapper
  * functions. An example is `ftl::rgbd::Frame`.
- * 
+ *
  * @see https://gitlab.utu.fi/nicolas.pope/ftl/-/wikis/Design/Frames
  */
 class Frame {
@@ -330,7 +330,7 @@ class Frame {
 	 * channel does not exist or if the template type does not match the content
 	 * then it throws an exception. The data can either be within this frame,
 	 * or if not in the frame then it checks the persistent store.
-	 * 
+	 *
 	 * The data may internally still be encoded and will only be decoded on the
 	 * first call to `get`. It is therefore strongly advised that any initial
 	 * calls to `get` are not concurrent as it will not be thread-safe.
@@ -431,7 +431,7 @@ class Frame {
 	/**
 	 * Create a change but with encoded data provided. This allows for both
 	 * lazy decode and for subsequent data forwarding without encoding.
-	 * 
+	 *
 	 * Currently unused.
 	 */
 	template <typename T>
@@ -441,7 +441,7 @@ class Frame {
 	 * Create a channel, mark with the given change type and provided encoded
 	 * data. Does not decode the data as it does not know the actually data
 	 * type of this channel at this time.
-	 * 
+	 *
 	 * To be used by `receiver`.
 	 * @see ftl::stream::Receiver
 	 */
@@ -517,7 +517,7 @@ class Frame {
 	 * do generate a change event but do no subsequently generate a flush event
 	 * as they are considered completed changes. This prevents loops whilst
 	 * ensuring everyone has a copy of the change.
-	 * 
+	 *
 	 * @see changeType
 	 */
 	inline ftl::Handle onFlush(const std::function<bool(Frame&,ftl::codecs::Channel)> &cb);
diff --git a/components/structures/src/new_frame.cpp b/components/structures/src/new_frame.cpp
index 1b1700d792d77dac74d106a623700daabb214ad3..09b91e3af8503223e34e834db8d97688c2f397e9 100644
--- a/components/structures/src/new_frame.cpp
+++ b/components/structures/src/new_frame.cpp
@@ -268,25 +268,29 @@ void Frame::store() {
 
 	if (!parent_) return;
 
-	UNIQUE_LOCK(parent_->mutex(), lk);
+	{
+		UNIQUE_LOCK(parent_->mutex(), lk);
+		for (auto c : changed_) {
+			if (ftl::data::isPersistent(c.first) && hasOwn(c.first)) {
+				auto &d = data_[c.first];
+				auto &pd = parent_->data_[c.first];
+				pd.data = std::move(d.data);
+				pd.encoded = std::move(d.encoded);
+				//if (d.status == ChannelStatus::ENCODED) LOG(INFO) << "STORE ENCODED: " << (int)c.first;
+				pd.status = ChannelStatus::VALID;
+				//data_.erase(c.first);
+				d.status = ChannelStatus::INVALID;
+			}
 
-	for (auto c : changed_) {
-		if (ftl::data::isPersistent(c.first) && hasOwn(c.first)) {
-			auto &d = data_[c.first];
-			auto &pd = parent_->data_[c.first];
-			pd.data = std::move(d.data);
-			pd.encoded = std::move(d.encoded);
-			//if (d.status == ChannelStatus::ENCODED) LOG(INFO) << "STORE ENCODED: " << (int)c.first;
-			pd.status = ChannelStatus::VALID;
-			//data_.erase(c.first);
-			d.status = ChannelStatus::INVALID;
+			uint64_t sig = (uint64_t(id()) << 32) + static_cast<unsigned int>(c.first);
+			const auto &i = parent_->change_channel_.find(sig);
+
+			if (i != parent_->change_channel_.end()) i->second.trigger(*this, c.first);
 		}
+	}
 
+	for (auto c : changed_) {
 		parent_->change_.trigger(*this, c.first);
-		uint64_t sig = (uint64_t(id()) << 32) + static_cast<unsigned int>(c.first);
-		const auto &i = parent_->change_channel_.find(sig);
-
-		if (i != parent_->change_channel_.end()) i->second.trigger(*this, c.first);
 	}
 }