Skip to content

Commit 03f5356

Browse files
committed
Add snapshot unique_key hint for duplicate row errors
1 parent 1d283ef commit 03f5356

3 files changed

Lines changed: 67 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: Fixes
2+
body: "Add a snapshot-specific hint when duplicate rows during DML suggest a non-unique `unique_key`"
3+
time: 2026-05-18T07:52:56.000000-07:00
4+
custom:
5+
Author: saitejadesu
6+
Issue: "10864"

core/dbt/task/snapshot.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,27 @@
1313
from dbt_common.utils import cast_dict_to_dict_of_strings
1414

1515

16+
_DUPLICATE_ROW_DML_ERROR = "duplicate row detected during dml action"
17+
SNAPSHOT_UNIQUE_KEY_HINT = (
18+
"Hint: This can happen when the snapshot's configured `unique_key` is not unique. "
19+
"Ensure the `unique_key` column(s) identify a single row in the snapshot query."
20+
)
21+
22+
23+
def _add_snapshot_unique_key_hint(message: str) -> str:
24+
if _DUPLICATE_ROW_DML_ERROR not in message.lower() or SNAPSHOT_UNIQUE_KEY_HINT in message:
25+
return message
26+
return f"{message}\n\n{SNAPSHOT_UNIQUE_KEY_HINT}"
27+
28+
1629
class SnapshotRunner(ModelRunner):
1730
def describe_node(self) -> str:
1831
return "snapshot {}".format(self.get_node_representation())
1932

33+
def handle_exception(self, e: Exception, ctx) -> str:
34+
message = super().handle_exception(e, ctx)
35+
return _add_snapshot_unique_key_hint(message)
36+
2037
def print_result_line(self, result):
2138
model = result.node
2239
group = group_lookup.get(model.unique_id)

tests/unit/task/test_snapshot.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from unittest.mock import patch
2+
3+
from dbt.task.run import ModelRunner
4+
from dbt.task.snapshot import (
5+
SNAPSHOT_UNIQUE_KEY_HINT,
6+
SnapshotRunner,
7+
_add_snapshot_unique_key_hint,
8+
)
9+
10+
11+
def test_add_snapshot_unique_key_hint_for_duplicate_row_dml_error():
12+
message = "Database Error\n Duplicate row detected during DML action"
13+
14+
updated_message = _add_snapshot_unique_key_hint(message)
15+
16+
assert message in updated_message
17+
assert SNAPSHOT_UNIQUE_KEY_HINT in updated_message
18+
19+
20+
def test_add_snapshot_unique_key_hint_only_once():
21+
message = (
22+
"Database Error\n"
23+
" Duplicate row detected during DML action\n\n"
24+
f"{SNAPSHOT_UNIQUE_KEY_HINT}"
25+
)
26+
27+
assert _add_snapshot_unique_key_hint(message) == message
28+
29+
30+
def test_add_snapshot_unique_key_hint_ignores_other_errors():
31+
message = "Database Error\n relation does not exist"
32+
33+
assert _add_snapshot_unique_key_hint(message) == message
34+
35+
36+
def test_snapshot_runner_adds_unique_key_hint_to_duplicate_row_error():
37+
message = "Database Error\n Duplicate row detected during DML action"
38+
runner = object.__new__(SnapshotRunner)
39+
40+
with patch.object(ModelRunner, "handle_exception", return_value=message):
41+
updated_message = runner.handle_exception(Exception("database failed"), object())
42+
43+
assert message in updated_message
44+
assert SNAPSHOT_UNIQUE_KEY_HINT in updated_message

0 commit comments

Comments
 (0)