Fix wellness sync crash: serialize intraday_hr as JSON string for psycopg2
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 4s
Build and push images / build-frontend (push) Successful in 5s

psycopg2 treats Python lists as PostgreSQL arrays (bigint[]) rather than JSON,
causing a DatatypeMismatch error on the json/jsonb column. Serializing with
json.dumps() before the raw SQL INSERT fixes the type error.

Also wrap per-day INSERT in try/except+rollback so one bad day doesn't abort
the entire session, and add db.rollback() in tasks.py after sync_wellness
failure so the final status-update commit can always succeed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 11:00:22 +01:00
parent d57054509c
commit 37ffd4c9e0
2 changed files with 18 additions and 7 deletions
+17 -7
View File
@@ -253,6 +253,12 @@ def sync_wellness(garmin, user_id: int, since: Optional[datetime], db,
if not row:
continue
# psycopg2 treats Python lists as PostgreSQL arrays; serialize JSON columns
# explicitly so they arrive as a JSON string that the json/jsonb column accepts.
import json as _json
if "intraday_hr" in row and isinstance(row["intraday_hr"], list):
row["intraday_hr"] = _json.dumps(row["intraday_hr"])
cols = list(row.keys())
col_sql = ", ".join(cols)
val_sql = ", ".join(f":{c}" for c in cols)
@@ -267,13 +273,17 @@ def sync_wellness(garmin, user_id: int, since: Optional[datetime], db,
params = {"user_id": user_id, "day": day.isoformat()}
params.update(row)
db.execute(text(f"""
INSERT INTO health_metrics (user_id, date, {col_sql})
VALUES (:user_id, :day, {val_sql})
ON CONFLICT (user_id, date) DO UPDATE SET {upd_sql}
"""), params)
db.commit()
processed += 1
try:
db.execute(text(f"""
INSERT INTO health_metrics (user_id, date, {col_sql})
VALUES (:user_id, :day, {val_sql})
ON CONFLICT (user_id, date) DO UPDATE SET {upd_sql}
"""), params)
db.commit()
processed += 1
except Exception as exc:
logger.warning("Failed to upsert health_metrics for %s: %s", day_str, exc)
db.rollback()
return processed
+1
View File
@@ -530,6 +530,7 @@ def sync_garmin_connect_user(user_id: int):
)
except Exception as exc:
errors.append(f"wellness: {exc}")
db.rollback() # recover session so the final status commit can succeed
cfg.last_sync_at = datetime.now(timezone.utc)
cfg.last_sync_status = (