diff --git a/changelog.d/5808.misc b/changelog.d/5808.misc
new file mode 100644
index 0000000000000000000000000000000000000000..cac3fd34d12e2521bceb5536226073f31e7730f0
--- /dev/null
+++ b/changelog.d/5808.misc
@@ -0,0 +1 @@
+Handle incorrectly encoded query params correctly by returning a 400.
diff --git a/synapse/http/servlet.py b/synapse/http/servlet.py
index f0ca7d9aba44404ae16c8b2d6d24c50be3de70c7..fd07bf7b8e5572d36d0a536e2f2c56d460b19413 100644
--- a/synapse/http/servlet.py
+++ b/synapse/http/servlet.py
@@ -166,7 +166,12 @@ def parse_string_from_args(
         value = args[name][0]
 
         if encoding:
-            value = value.decode(encoding)
+            try:
+                value = value.decode(encoding)
+            except ValueError:
+                raise SynapseError(
+                    400, "Query parameter %r must be %s" % (name, encoding)
+                )
 
         if allowed_values is not None and value not in allowed_values:
             message = "Query parameter %r must be one of [%s]" % (