diff --git a/Dockerfile b/Dockerfile
index e41b7d5843d5a31d0aa47ed4afde4e7423b0b82f..34714ff57a0213640ac70e9f06f18eb5c9bb3795 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -24,12 +24,6 @@ COPY . .
 
 VOLUME ["/app"]
 
-RUN adduser --home /app --no-create-home --system --uid ${AURA_UID} --group app
-RUN mkdir -p /app/logs
-RUN chown -R app:app /app
-
-USER app
-
 # run with Django's development server
 CMD ["run.dev"]
 
@@ -37,6 +31,12 @@ FROM base AS prod
 
 COPY . .
 
+RUN adduser --home /app --no-create-home --system --uid ${AURA_UID} --group app
+RUN mkdir -p /app/logs
+RUN mkdir -p /app/static/admin
+RUN chown -R app:app /app
+
+USER app
 # run with gunicorn
 CMD ["run.prod"]
 
diff --git a/Makefile b/Makefile
index 307b334030973fb64e019e7fe830b0db69e5b303..e17ab421c64212a4d72515f93b83e867c141fbae 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@ create_oidc_client.dashboard:
 create_oidc_client.tank:
 	$(POETRY_RUN_MANAGE) create_oidc_client tank confidential --client-id ${TANK_OIDC_CLIENT_ID} --client-secret ${TANK_OIDC_CLIENT_SECRET} -r "code" -u ${TANK_CALLBACK_BASE_URL}/tank/auth/oidc/callback
 
-initialize: migrate loaddata.program create_oidc_client.dashboard create_oidc_client.tank
+initialize: migrate collectstatic loaddata.program create_oidc_client.dashboard create_oidc_client.tank
 	$(POETRY_RUN_MANAGE) createsuperuser --no-input
 	$(POETRY_RUN_MANAGE) creatersakey
 
diff --git a/program/serializers.py b/program/serializers.py
index ea70b69b4e178382c129e0b673a45e99eda6d25f..a5383bff21b2e531d97da4425282db95b7d60cfa 100644
--- a/program/serializers.py
+++ b/program/serializers.py
@@ -548,22 +548,23 @@ class ShowSerializer(serializers.HyperlinkedModelSerializer):
         instance.slug = validated_data.get("slug", instance.slug)
         instance.type = validated_data.get("type_id", instance.type)
 
-        if category := validated_data.get("category"):
+        # optional many-to-many in PATCH requests
+        if (category := validated_data.get("category")) is not None:
             instance.category.set(category)
 
-        if hosts := validated_data.get("hosts"):
+        if (hosts := validated_data.get("hosts")) is not None:
             instance.hosts.set(hosts)
 
-        if language := validated_data.get("language"):
+        if (language := validated_data.get("language")) is not None:
             instance.language.set(language)
 
-        if music_focus := validated_data.get("music_focus"):
+        if (music_focus := validated_data.get("music_focus")) is not None:
             instance.music_focus.set(music_focus)
 
-        if owners := validated_data.get("owners"):
+        if (owners := validated_data.get("owners")) is not None:
             instance.owners.set(owners)
 
-        if topic := validated_data.get("topic"):
+        if (topic := validated_data.get("topic")) is not None:
             instance.topic.set(topic)
 
         if links_data := validated_data.get("links"):
@@ -779,7 +780,6 @@ class TimeSlotSerializer(serializers.ModelSerializer):
         instance.repetition_of = validated_data.get("repetition_of_id", instance.repetition_of)
         instance.playlist_id = validated_data.get("playlist_id", instance.playlist_id)
 
-
         instance.save()
         return instance
 
@@ -790,17 +790,15 @@ class NoteLinkSerializer(serializers.ModelSerializer):
         fields = ("type", "url")
 
 
-tags_json_schema = {
-  "type": "array",
-  "items": {
-    "type": "string"
-  }
-}
+tags_json_schema = {"type": "array", "items": {"type": "string"}}
 
 
 class NoteSerializer(serializers.ModelSerializer):
     contributor_ids = serializers.PrimaryKeyRelatedField(
-        many=True, queryset=Host.objects.all(), required=False, source="contributors",
+        many=True,
+        queryset=Host.objects.all(),
+        required=False,
+        source="contributors",
     )
     image_id = serializers.PrimaryKeyRelatedField(
         queryset=Image.objects.all(), required=False, allow_null=True
@@ -864,6 +862,7 @@ class NoteSerializer(serializers.ModelSerializer):
 
         show = validated_data["timeslot"].schedule.show
 
+        # we derive `contributors`, `language` and `topic` from the Show's values if not set
         contributors = validated_data.pop("contributors", show.owners.values_list("id", flat=True))
         language = validated_data.pop("language", show.language.values_list("id", flat=True))
         topic = validated_data.pop("topic", show.topic.values_list("id", flat=True))
@@ -905,9 +904,16 @@ class NoteSerializer(serializers.ModelSerializer):
         instance.tags = validated_data.get("tags", instance.tags)
         instance.title = validated_data.get("title", instance.title)
 
-        if contributors := validated_data.get("contributors", []):
+        # optional many-to-many in PATCH requests
+        if (contributors := validated_data.get("contributors")) is not None:
             instance.contributors.set(contributors)
 
+        if (language := validated_data.get("language")) is not None:
+            instance.language.set(language)
+
+        if (topic := validated_data.get("topic")) is not None:
+            instance.topic.set(topic)
+
         if cba_id := validated_data.get("cba_id"):
             if audio_url := get_audio_url(cba_id):
                 NoteLink.objects.create(note=instance, type="CBA", url=audio_url)
@@ -918,12 +924,6 @@ class NoteSerializer(serializers.ModelSerializer):
             for link_data in links_data:
                 NoteLink.objects.create(note=instance, **link_data)
 
-        if language := validated_data.get("language", []):
-            instance.language.set(language)
-
-        if topic := validated_data.get("topic", []):
-            instance.topic.set(topic)
-
         instance.updated_by = self.context.get("request").user.username
 
         instance.save()
diff --git a/program/views.py b/program/views.py
index 3b382ce1ca4dd1628c28900ffff18d16f5f21222..ca61dc3bf60c58e2741c345ef621054502903591 100644
--- a/program/views.py
+++ b/program/views.py
@@ -33,7 +33,7 @@ from rest_framework.pagination import LimitOffsetPagination
 from rest_framework.response import Response
 
 from django.contrib.auth.models import User
-from django.http import Http404, HttpResponse
+from django.http import Http404, HttpResponse, JsonResponse
 from django.shortcuts import get_object_or_404
 from django.utils import timezone
 from django.utils.translation import gettext as _
@@ -457,6 +457,7 @@ class APIRRuleViewSet(viewsets.ModelViewSet):
                       (`one-solution-per-conflict`). Only one solution is allowed per conflict,
                       so you either offered too many or not enough solutions for any reported
                       conflicts.
+                    * The referenced recurrence rule does not exist.
                       """
                 ),
             ),
@@ -561,6 +562,10 @@ class APIScheduleViewSet(
 
         try:
             resolution = resolve_conflicts(request.data, pk, show_pk)
+        # FIXME: Find a better way to do this.
+        # The exception is thrown by the instantiate_upcoming_schedule function.
+        except RRule.DoesNotExist as exc:
+            return JsonResponse({"rruleId": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
         except ScheduleConflictError as exc:
             return Response(exc.conflicts, status.HTTP_409_CONFLICT)