Skip to content

Commit 8d9e3dc

Browse files
authored
fix: handle deleted node during dispatch (baserow#5051)
* Ensure dispatch does crash when node doesn't exist. * Improve logger message when node doesn't exist.
1 parent d17c292 commit 8d9e3dc

3 files changed

Lines changed: 55 additions & 5 deletions

File tree

backend/src/baserow/contrib/automation/nodes/handler.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -424,12 +424,27 @@ def dispatch_node(
424424
logger.error(str(e))
425425
return None
426426

427-
node = self.get_node(node_id)
428-
simulate_until_node = (
429-
node.workflow.get_graph().get_node(workflow_history.simulate_until_node_id)
430-
if workflow_history.simulate_until_node_id
431-
else None
427+
error = (
428+
"Node with ID {} was not found. The node was likely "
429+
"deleted before the task was executed."
432430
)
431+
try:
432+
node = self.get_node(node_id)
433+
except AutomationNodeDoesNotExist:
434+
logger.warning(error.format(node_id))
435+
return None
436+
437+
try:
438+
simulate_until_node = (
439+
node.workflow.get_graph().get_node(
440+
workflow_history.simulate_until_node_id
441+
)
442+
if workflow_history.simulate_until_node_id
443+
else None
444+
)
445+
except AutomationNodeDoesNotExist:
446+
logger.warning(error.format(workflow_history.simulate_until_node_id))
447+
return None
433448

434449
if simulate_until_node:
435450
allowed_nodes = {

backend/tests/baserow/contrib/automation/nodes/test_node_dispatch_async.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,3 +1456,29 @@ def test_dispatch_node_iterator_with_no_rows(data_fixture):
14561456
# Ensure we never return an empty chain, which would cause
14571457
# self.replace() to crash with an error.
14581458
assert result is None
1459+
1460+
1461+
@pytest.mark.django_db
1462+
@patch(f"{NODE_HANDLER_PATH}.logger")
1463+
def test_dispatch_node_with_deleted_node(mock_logger, data_fixture):
1464+
"""
1465+
In the rare case where a node is deleted between the time a dispatch
1466+
is queued and when the task actually runs, we should handle this
1467+
gracefully instead of crashing.
1468+
"""
1469+
1470+
data = create_workflow(data_fixture)
1471+
action_node = data["action_node"]
1472+
history = data["workflow_history"]
1473+
1474+
# delete the node to simulate a race condition
1475+
action_node_id = action_node.id
1476+
action_node.delete()
1477+
1478+
result = AutomationNodeHandler().dispatch_node(action_node_id, history.id)
1479+
assert result is None
1480+
expected_error = (
1481+
f"Node with ID {action_node_id} was not found. The node was likely "
1482+
"deleted before the task was executed."
1483+
)
1484+
mock_logger.warning.assert_called_once_with(expected_error)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "bug",
3+
"message": "Fixed a bug that caused a crash due to a race condition that could happen if a node is deleted while it is being dispatched.",
4+
"issue_origin": "github",
5+
"issue_number": null,
6+
"domain": "automation",
7+
"bullet_points": [],
8+
"created_at": "2026-03-26"
9+
}

0 commit comments

Comments
 (0)