From 2d94f9935655d111e858e54ebb78508530a6e7d3 Mon Sep 17 00:00:00 2001 From: owain Date: Mon, 8 Jun 2026 12:11:55 +0100 Subject: [PATCH] Fix activity dedup crash: MultipleResultsFound on same-day same-sport activities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous query used >= on start_time with no upper bound, so it matched ALL activities of that sport type starting after the given minute on that day — crashing with MultipleResultsFound whenever two such activities existed. Fix: bound the window to ±60s from start_time and use .scalars().first() so the query returns at most one Activity rather than raising on duplicates. Co-Authored-By: Claude Sonnet 4.6 --- backend/app/workers/tasks.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/app/workers/tasks.py b/backend/app/workers/tasks.py index 80ac5cd..1228553 100644 --- a/backend/app/workers/tasks.py +++ b/backend/app/workers/tasks.py @@ -82,15 +82,16 @@ def process_activity_file(self, file_path: str, user_id: int, source_type: str, with SyncSessionLocal() as db: start_time = datetime.fromisoformat(parsed["start_time"]) - # Deduplicate by (user_id, sport_type, start_time date + within 60s) + # Deduplicate: same user + sport_type + start_time within ±60s + from datetime import timedelta existing = db.execute( select(Activity).where( Activity.user_id == user_id, Activity.sport_type == parsed["sport_type"], - func.date(Activity.start_time) == start_time.date(), - Activity.start_time >= start_time.replace(second=0, microsecond=0), + Activity.start_time >= start_time - timedelta(seconds=60), + Activity.start_time <= start_time + timedelta(seconds=60), ) - ).scalar_one_or_none() + ).scalars().first() if existing: # Stamp garmin_activity_id if this came from a Garmin Connect sync