From e97bb03bdbdaee5dd3bcaf87c24a2f12de13a7f4 Mon Sep 17 00:00:00 2001 From: Manan Bhatt Date: Thu, 19 Mar 2026 11:34:49 +0530 Subject: [PATCH] Fix v1 fallback to also handle HTTP 405 from update-task-v2 endpoint On Conductor instances older than v5, POSTing to /tasks/update-v2 returns 405 (Method Not Allowed) rather than 404 (Not Found). This happens because the wildcard GET handler @GetMapping("/{taskId}") matches the path, causing Spring MVC to reject the POST with 405 instead of a plain 404. The previous fix only checked for status 404, so the fallback to the v1 update endpoint never triggered on Conductor 4.x, leaving task updates failing after 4 retries. Changes: - Check e.status in (404, 405) in both TaskRunner and AsyncTaskRunner - Log the actual HTTP status code in the warning message - Add unit test covering the 405 fallback case --- .../client/automator/async_task_runner.py | 7 +++--- src/conductor/client/automator/task_runner.py | 7 +++--- .../automator/test_task_runner_coverage.py | 23 +++++++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/conductor/client/automator/async_task_runner.py b/src/conductor/client/automator/async_task_runner.py index 41e7ab56..c0e601f2 100644 --- a/src/conductor/client/automator/async_task_runner.py +++ b/src/conductor/client/automator/async_task_runner.py @@ -841,11 +841,12 @@ async def __async_update_task(self, task_result: TaskResult): ) return None except ApiException as e: - if e.status == 404 and self._use_update_v2: + if e.status in (404, 405) and self._use_update_v2: logger.warning( - "Server does not support update-task-v2 endpoint (HTTP 404). " + "Server does not support update-task-v2 endpoint (HTTP %d). " "Falling back to v1 update endpoint. " - "Upgrade your Conductor instance to v5+ to enable the v2 endpoint." + "Upgrade your Conductor instance to v5+ to enable the v2 endpoint.", + e.status, ) self._use_update_v2 = False # Retry immediately with v1 diff --git a/src/conductor/client/automator/task_runner.py b/src/conductor/client/automator/task_runner.py index 7168edf5..dbc97f18 100644 --- a/src/conductor/client/automator/task_runner.py +++ b/src/conductor/client/automator/task_runner.py @@ -871,11 +871,12 @@ def __update_task(self, task_result: TaskResult): ) return None except ApiException as e: - if e.status == 404 and self._use_update_v2: + if e.status in (404, 405) and self._use_update_v2: logger.warning( - "Server does not support update-task-v2 endpoint (HTTP 404). " + "Server does not support update-task-v2 endpoint (HTTP %d). " "Falling back to v1 update endpoint. " - "Upgrade your Orkes instance to v5+ to enable the v2 endpoint." + "Upgrade your Orkes instance to v5+ to enable the v2 endpoint.", + e.status, ) self._use_update_v2 = False # Retry immediately with v1 diff --git a/tests/unit/automator/test_task_runner_coverage.py b/tests/unit/automator/test_task_runner_coverage.py index d7b0ba91..e2d474bf 100644 --- a/tests/unit/automator/test_task_runner_coverage.py +++ b/tests/unit/automator/test_task_runner_coverage.py @@ -829,6 +829,29 @@ def test_update_task_v2_404_falls_back_to_v1(self): mock_v1.assert_called_once() self.assertIsNone(result) + @patch('time.sleep', Mock(return_value=None)) + def test_update_task_v2_405_falls_back_to_v1(self): + """When server returns 405 for v2 endpoint (older Conductor), should fall back to v1.""" + worker = MockWorker('test_task') + task_runner = TaskRunner(worker=worker) + + task_result = TaskResult( + task_id='test_id', + workflow_instance_id='wf_id', + worker_id=worker.get_identity(), + status=TaskResultStatus.COMPLETED + ) + + with patch.object(TaskResourceApi, 'update_task_v2', + side_effect=ApiException(status=405)) as mock_v2, \ + patch.object(TaskResourceApi, 'update_task', return_value='ok') as mock_v1: + result = task_runner._TaskRunner__update_task(task_result) + + mock_v2.assert_called_once() + mock_v1.assert_called_once() + self.assertIsNone(result) + self.assertFalse(task_runner._use_update_v2) + @patch('time.sleep', Mock(return_value=None)) def test_update_task_v2_404_sets_v1_flag(self): """After a 404 on v2, _use_update_v2 flag must be False."""