diff --git a/program/exceptions.py b/program/exceptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..b1ad279f155af65d3c2e60f0910cc2a83b487e4b
--- /dev/null
+++ b/program/exceptions.py
@@ -0,0 +1,10 @@
+from rest_framework import status
+from rest_framework.exceptions import ValidationError
+
+from django.utils.translation import gettext_lazy as _
+
+
+class ConfigurationError(ValidationError):
+    status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
+    default_detail = _("Invalid or insufficient server configuration.")
+    default_code = "misconfigured"
diff --git a/program/services.py b/program/services.py
index 86b9f0cd0434010c75b9281df0290856963b261c..87693930e93d08256d2b2c79e48c0ca467ea01a9 100644
--- a/program/services.py
+++ b/program/services.py
@@ -30,6 +30,7 @@ from django.core.exceptions import ObjectDoesNotExist
 from django.db.models import Q, QuerySet
 from django.utils import timezone
 from django.utils.translation import gettext_lazy as _
+from program.exceptions import ConfigurationError
 from program.models import (
     Note,
     ProgramEntry,
@@ -745,7 +746,10 @@ def generate_program_entries(
     radio_settings: RadioSettings | None = RadioSettings.objects.first()
     fallback_show = radio_settings.fallback_show if radio_settings is not None else None
     if fallback_show is None:
-        raise ValueError("Radio settings must set fallback show if include_virtual is True.")
+        raise ConfigurationError(
+            "Radio settings must define a fallback show if include_virtual is True.",
+            code="no-fallback-show-defined",
+        )
 
     entry_start = start
     timeslot: TimeSlot