From 7d5e7b1625c7f0356cb9b965ffb88df2fade0a09 Mon Sep 17 00:00:00 2001 From: stumpylog <797416+stumpylog@users.noreply.github.com> Date: Tue, 3 Nov 2026 11:33:16 -0800 Subject: [PATCH] Fix: keep Celery's Django task base for file move scoped tasks Passing task_cls to Celery replaces the Django fixup's default DjangoTask, so FileMoveScopedTask, built on the plain Task, left every task without delay_on_commit. The workflow AI suggestions trigger failed with an AttributeError. FileMoveScopedTask now subclasses DjangoTask, which only needs django.db.transaction and is safe to import before apps load. The task scope test also asserts tasks are DjangoTask instances. --- src/documents/tests/test_file_moves.py | 3 +++ src/paperless/celery.py | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/documents/tests/test_file_moves.py b/src/documents/tests/test_file_moves.py index ce6829dbfa..3106063af7 100644 --- a/src/documents/tests/test_file_moves.py +++ b/src/documents/tests/test_file_moves.py @@ -9,6 +9,7 @@ from typing import TYPE_CHECKING import pytest from asgiref.sync import async_to_sync from asgiref.sync import sync_to_async +from celery.contrib.django.task import DjangoTask from django.db import transaction from django.http import HttpRequest from django.http import HttpResponse @@ -777,10 +778,12 @@ class TestCeleryTaskScope: - A registered task is inspected THEN: - It runs through FileMoveScopedTask + - It keeps Celery's DjangoTask API, such as delay_on_commit """ from paperless.celery import FileMoveScopedTask assert isinstance(bulk_update_documents, FileMoveScopedTask) + assert isinstance(bulk_update_documents, DjangoTask) def test_failing_task_still_moves_committed_change( self, diff --git a/src/paperless/celery.py b/src/paperless/celery.py index dab9217a07..cc2dd81799 100644 --- a/src/paperless/celery.py +++ b/src/paperless/celery.py @@ -1,7 +1,7 @@ import os from celery import Celery -from celery import Task +from celery.contrib.django.task import DjangoTask from celery.signals import worker_process_init from kombu.serialization import register @@ -28,12 +28,15 @@ register( ) -class FileMoveScopedTask(Task): +class FileMoveScopedTask(DjangoTask): """ Run every task inside a document file move scope, so each document a task changes is moved once when the task ends, after its writes have committed. The scope also flushes when the task raises, for writes that committed before the failure. + + Subclasses DjangoTask because a custom task_cls replaces the Django fixup's + default, and delay_on_commit lives there. """ def __call__(self, *args, **kwargs):