diff --git a/src/client_server/media.rs b/src/client_server/media.rs
index 0c23488428c409814134e2b821e7e58f10a0fe2f..e6bd182b981189a854fc6e923b335a67c846c05a 100644
--- a/src/client_server/media.rs
+++ b/src/client_server/media.rs
@@ -66,7 +66,7 @@ pub async fn get_content_route(
     {
         Ok(get_content::Response {
             file,
-            content_type: Some(content_type),
+            content_type,
             content_disposition: filename,
         }
         .into())
@@ -116,11 +116,7 @@ pub async fn get_content_thumbnail_route(
             .try_into()
             .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?,
     )? {
-        Ok(get_content_thumbnail::Response {
-            file,
-            content_type: Some(content_type),
-        }
-        .into())
+        Ok(get_content_thumbnail::Response { file, content_type }.into())
     } else if &*body.server_name != db.globals.server_name() && body.allow_remote {
         let get_thumbnail_response = server_server::send_request(
             &db.globals,
diff --git a/src/client_server/membership.rs b/src/client_server/membership.rs
index 849fb7e2a38bc49d9f7eb4f500df95257fc5ceff..47fcde1ae94001d3a9917b0ed1ceab05cdf7bf7b 100644
--- a/src/client_server/membership.rs
+++ b/src/client_server/membership.rs
@@ -510,8 +510,7 @@ async fn join_room_by_id_helper(
             .expect("event is valid, we just created it"),
         );
 
-        // TODO fixup CanonicalJsonValue
-        // use that instead of serde_json::Map... maybe?
+        // Convert `serde_json;:Value` to `CanonicalJsonObj` for hashing/signing
         let mut canon_json_stub: BTreeMap<_, ruma::signatures::CanonicalJsonValue> =
             serde_json::from_value(join_event_stub_value).expect("json Value is canonical JSON");
 
diff --git a/src/database/media.rs b/src/database/media.rs
index bfc6207874dee888002ee2e655fe2b3bb91be1b1..89d48e16f2f8880cd32791443987c98f8db4d821 100644
--- a/src/database/media.rs
+++ b/src/database/media.rs
@@ -5,7 +5,7 @@
 
 pub struct FileMeta {
     pub filename: Option<String>,
-    pub content_type: String,
+    pub content_type: Option<String>,
     pub file: Vec<u8>,
 }
 
@@ -83,12 +83,14 @@ pub fn get(&self, mxc: &str) -> Result<Option<FileMeta>> {
             let (key, file) = r?;
             let mut parts = key.rsplit(|&b| b == 0xff);
 
-            let content_type = utils::string_from_bytes(
-                parts
-                    .next()
-                    .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?,
-            )
-            .map_err(|_| Error::bad_database("Content type in mediaid_file is invalid unicode."))?;
+            let content_type = parts
+                .next()
+                .map(|bytes| {
+                    Ok::<_, Error>(utils::string_from_bytes(bytes).map_err(|_| {
+                        Error::bad_database("Content type in mediaid_file is invalid unicode.")
+                    })?)
+                })
+                .transpose()?;
 
             let filename_bytes = parts
                 .next()
@@ -158,12 +160,14 @@ pub fn get_thumbnail(&self, mxc: String, width: u32, height: u32) -> Result<Opti
             let (key, file) = r?;
             let mut parts = key.rsplit(|&b| b == 0xff);
 
-            let content_type = utils::string_from_bytes(
-                parts
-                    .next()
-                    .ok_or_else(|| Error::bad_database("Invalid Media ID in db"))?,
-            )
-            .map_err(|_| Error::bad_database("Content type in mediaid_file is invalid unicode."))?;
+            let content_type = parts
+                .next()
+                .map(|bytes| {
+                    Ok::<_, Error>(utils::string_from_bytes(bytes).map_err(|_| {
+                        Error::bad_database("Content type in mediaid_file is invalid unicode.")
+                    })?)
+                })
+                .transpose()?;
 
             let filename_bytes = parts
                 .next()
@@ -189,12 +193,14 @@ pub fn get_thumbnail(&self, mxc: String, width: u32, height: u32) -> Result<Opti
             let (key, file) = r?;
             let mut parts = key.rsplit(|&b| b == 0xff);
 
-            let content_type = utils::string_from_bytes(
-                parts
-                    .next()
-                    .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?,
-            )
-            .map_err(|_| Error::bad_database("Content type in mediaid_file is invalid unicode."))?;
+            let content_type = parts
+                .next()
+                .map(|bytes| {
+                    Ok::<_, Error>(utils::string_from_bytes(bytes).map_err(|_| {
+                        Error::bad_database("Content type in mediaid_file is invalid unicode.")
+                    })?)
+                })
+                .transpose()?;
 
             let filename_bytes = parts
                 .next()
diff --git a/src/database/rooms.rs b/src/database/rooms.rs
index 3d5b890b890fc73d39e359fc1d73f8d9fe7b76a2..a95587cc27b5bba8f572cedf78ea66fbff2caee3 100644
--- a/src/database/rooms.rs
+++ b/src/database/rooms.rs
@@ -499,7 +499,6 @@ pub fn replace_pdu_leaves(&self, room_id: &RoomId, event_id: &EventId) -> Result
         Ok(())
     }
 
-    #[allow(clippy::too_many_arguments)]
     /// Creates a new persisted data unit and adds it to a room.
     ///
     /// By this point the incoming event should be fully authenticated, no auth happens
@@ -856,9 +855,8 @@ pub fn build_and_append_pdu(
         };
 
         // Hash and sign
-        let mut pdu_json: BTreeMap<String, ruma::serde::CanonicalJsonValue> =
-            serde_json::from_value(serde_json::json!(&pdu))
-                .expect("event is valid, we just created it");
+        let mut pdu_json =
+            utils::to_canonical_object(&pdu).expect("event is valid, we just created it");
 
         pdu_json.remove("event_id");
 
diff --git a/src/utils.rs b/src/utils.rs
index edcf48a10470115954fed196e67a6a08115d5650..c82e6feb8f26ebc1924053227a1b02cf7b7a55a2 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,6 +1,7 @@
 use argon2::{Config, Variant};
 use cmp::Ordering;
 use rand::prelude::*;
+use ruma::serde::{try_from_json_map, CanonicalJsonError, CanonicalJsonObject};
 use sled::IVec;
 use std::{
     cmp,
@@ -94,3 +95,19 @@ pub fn common_elements(
             .all(|b| b)
     }))
 }
+
+/// Fallible conversion from any value that implements `Serialize` to a `CanonicalJsonObject`.
+///
+/// `value` must serialize to an `serde_json::Value::Object`.
+pub fn to_canonical_object<T: serde::Serialize>(
+    value: T,
+) -> Result<CanonicalJsonObject, CanonicalJsonError> {
+    use serde::ser::Error;
+
+    match serde_json::to_value(value).map_err(CanonicalJsonError::SerDe)? {
+        serde_json::Value::Object(map) => try_from_json_map(map),
+        _ => Err(CanonicalJsonError::SerDe(serde_json::Error::custom(
+            "Value must be an object",
+        ))),
+    }
+}