diff --git a/arkindex/ponos/migrations/0014_task_task_finished_requires_final_state.py b/arkindex/ponos/migrations/0014_task_task_finished_requires_final_state.py
new file mode 100644
index 0000000000000000000000000000000000000000..13dbe15627c24fa28c4abc8c337d8960c4c3fe18
--- /dev/null
+++ b/arkindex/ponos/migrations/0014_task_task_finished_requires_final_state.py
@@ -0,0 +1,43 @@
+# Generated by Django 5.0.8 on 2025-01-07 10:11
+
+from django.db import migrations, models
+
+from arkindex.ponos.models import State
+
+# Copy the FINAL_STATES here so that if we ever change them,
+# Django will detect it and require a new migration
+FINAL_STATES = (
+    State.Completed,
+    State.Failed,
+    State.Error,
+    State.Stopped,
+    State.Cancelled,
+)
+
+def clear_unexpected_finish_dates(apps, schema_editor):
+    Task = apps.get_model("ponos", "Task")
+    Task.objects.exclude(state__in=FINAL_STATES).exclude(finished=None).update(finished=None)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("ponos", "0013_task_ttl"),
+        ("process", "0046_workerrun_ttl"),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            clear_unexpected_finish_dates,
+            reverse_code=migrations.RunPython.noop,
+            elidable=True,
+        ),
+        migrations.AddConstraint(
+            model_name="task",
+            constraint=models.CheckConstraint(
+                check=models.Q(finished=None) | models.Q(state__in=FINAL_STATES),
+                name="task_finished_requires_final_state",
+                violation_error_message="Only tasks in a final state can have a finish date set.",
+            ),
+        ),
+    ]
diff --git a/arkindex/ponos/models.py b/arkindex/ponos/models.py
index c9d3e109f8818d65ee4ed5e03059cf7f51c7a4b4..e77e10df10c44acead36174613d930f84b16ae54 100644
--- a/arkindex/ponos/models.py
+++ b/arkindex/ponos/models.py
@@ -394,6 +394,11 @@ class Task(models.Model):
                 name="task_finished_after_started",
                 violation_error_message="The task finish date must not be earlier than the task start date.",
             ),
+            models.CheckConstraint(
+                check=Q(finished=None) | Q(state__in=FINAL_STATES),
+                name="task_finished_requires_final_state",
+                violation_error_message="Only tasks in a final state can have a finish date set.",
+            ),
         ]
 
     def __str__(self) -> str:
diff --git a/arkindex/ponos/tests/tasks/test_partial_update.py b/arkindex/ponos/tests/tasks/test_partial_update.py
index e43cc217ae4d2690c4862846aa66ae0ed79842ed..f4c8adbe30a6260adb86d1c18ff89f9943af60e6 100644
--- a/arkindex/ponos/tests/tasks/test_partial_update.py
+++ b/arkindex/ponos/tests/tasks/test_partial_update.py
@@ -83,6 +83,7 @@ class TestTaskPartialUpdate(FixtureAPITestCase):
         for (state_from, state_to) in self.docker_task_transitions:
             with self.subTest(state_from=state_from, state_to=state_to):
                 self.task1.state = state_from
+                self.task1.finished = None
                 self.task1.save()
                 resp = self.client.patch(
                     reverse("api:task-details", args=[self.task1.id]),
@@ -114,6 +115,7 @@ class TestTaskPartialUpdate(FixtureAPITestCase):
         for (state_from, state_to) in self.slurm_task_transitions:
             with self.subTest(state_from=state_from, state_to=state_to):
                 self.task1.state = state_from
+                self.task1.finished = None
                 self.task1.save()
                 resp = self.client.patch(
                     reverse("api:task-details", args=[self.task1.id]),
diff --git a/arkindex/ponos/tests/tasks/test_update.py b/arkindex/ponos/tests/tasks/test_update.py
index 97029d9324ec065e5005327b9975a48cf7aabc22..762a7fd31cac76e51dfad964cc3aee684902708c 100644
--- a/arkindex/ponos/tests/tasks/test_update.py
+++ b/arkindex/ponos/tests/tasks/test_update.py
@@ -501,6 +501,7 @@ class TestTaskUpdate(FixtureAPITestCase):
         for (state_from, state_to) in self.docker_task_transitions:
             with self.subTest(state_from=state_from, state_to=state_to):
                 self.task1.state = state_from
+                self.task1.finished = None
                 self.task1.save()
                 resp = self.client.put(
                     reverse("api:task-details", args=[self.task1.id]),
@@ -532,6 +533,7 @@ class TestTaskUpdate(FixtureAPITestCase):
         for (state_from, state_to) in self.slurm_task_transitions:
             with self.subTest(state_from=state_from, state_to=state_to):
                 self.task1.state = state_from
+                self.task1.finished = None
                 self.task1.save()
                 resp = self.client.put(
                     reverse("api:task-details", args=[self.task1.id]),