diff --git a/src/documents/tasks.py b/src/documents/tasks.py index 4f87c77c77..4321595c9a 100644 --- a/src/documents/tasks.py +++ b/src/documents/tasks.py @@ -718,18 +718,22 @@ def run_workflows_for_documents( id__in=document_ids, root_document__isnull=True, ): - run_workflows( - trigger_type=WorkflowTrigger.WorkflowTriggerType.MANUAL, - document=document, - workflow_to_run=workflow, - skip_matching=not filter_documents, - ) - # Manual runs dont send a document_updated signal, so push a websocket - # update here to ensure clients are refreshed - send_websocket_document_updated( - sender=None, - document=document, - ) + try: + run_workflows( + trigger_type=WorkflowTrigger.WorkflowTriggerType.MANUAL, + document=document, + workflow_to_run=workflow, + skip_matching=not filter_documents, + ) + # Manual runs dont send document_updated signal, so ws here to ensure clients are refreshed + send_websocket_document_updated( + sender=None, + document=document, + ) + except Exception: + logger.exception( + f"Error running workflow {workflow} on document {document.pk}", + ) def update_document_parent_tags(tag: Tag, new_parent: Tag) -> None: diff --git a/src/documents/tests/test_workflows.py b/src/documents/tests/test_workflows.py index 74d358a91c..78acc7b382 100644 --- a/src/documents/tests/test_workflows.py +++ b/src/documents/tests/test_workflows.py @@ -2338,6 +2338,33 @@ class TestWorkflows( with self.assertLogs("paperless.tasks", level="WARNING"): tasks.run_workflows_for_documents([doc.pk], 9999) + @mock.patch("documents.tasks.send_websocket_document_updated") + @mock.patch("documents.tasks.run_workflows") + def test_manual_workflow_run_continues_after_error( + self, + mock_run_workflows, + mock_ws, + ) -> None: + """A failure on one document does not abort the run for the others.""" + workflow = self._make_manual_workflow() + doc1 = DocumentFactory() + doc2 = DocumentFactory() + + def side_effect(*args, document, **kwargs): + if document.pk == doc1.pk: + raise Exception("boom") + + mock_run_workflows.side_effect = side_effect + + with self.assertLogs("paperless.tasks", level="ERROR"): + tasks.run_workflows_for_documents([doc1.pk, doc2.pk], workflow.pk) + + # Both documents were attempted; the websocket update only fired for the + # one that did not raise. + self.assertEqual(mock_run_workflows.call_count, 2) + ws_doc_ids = [c.kwargs["document"].pk for c in mock_ws.call_args_list] + self.assertEqual(ws_doc_ids, [doc2.pk]) + @mock.patch("documents.tasks.send_websocket_document_updated") def test_workflow_scheduled_trigger_sends_websocket_update( self,