From bce6e2742df97c6745d8f5acac4e000055186f0d Mon Sep 17 00:00:00 2001 From: Bmans Date: Wed, 17 Jun 2026 10:10:46 -0400 Subject: [PATCH 1/2] fix(import): return descriptive error messages and success response Previously the import endpoint returned null on success (displayed as "null" in the web UI) and a generic 500 on failure. This caused confusion about whether the import worked and gave no actionable error message. - api.py: raise a descriptive exception when importing a bucket that already exists (resolves the TODO comment) - rest.py: wrap import logic in try/except so errors surface as HTTP 400 with a human-readable message; return {"message": "Import successful"} on success instead of null Closes ActivityWatch/activitywatch#394 Co-Authored-By: Claude Sonnet 4.6 --- aw_server/api.py | 6 +++++- aw_server/rest.py | 26 +++++++++++++++----------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/aw_server/api.py b/aw_server/api.py index a143c0c..ddcdbc8 100644 --- a/aw_server/api.py +++ b/aw_server/api.py @@ -107,7 +107,11 @@ def import_bucket(self, bucket_data: Any): bucket_id = bucket_data["id"] logger.info(f"Importing bucket {bucket_id}") - # TODO: Check that bucket doesn't already exist + if bucket_id in self.db.buckets(): + raise Exception( + f"Bucket '{bucket_id}' already exists. Delete it first or rename the bucket before importing." + ) + self.db.create_bucket( bucket_id, type=bucket_data["type"], diff --git a/aw_server/rest.py b/aw_server/rest.py index 5a6b822..1d3089c 100644 --- a/aw_server/rest.py +++ b/aw_server/rest.py @@ -370,18 +370,22 @@ class ImportAllResource(Resource): @api.expect(buckets_export) @copy_doc(ServerAPI.import_all) def post(self): - # If import comes from a form in th web-ui - if len(request.files) > 0: - # web-ui form only allows one file, but technically it's possible to - # upload multiple files at the same time - for filename, f in request.files.items(): - buckets = json.loads(f.stream.read())["buckets"] + try: + # If import comes from a form in the web-ui + if len(request.files) > 0: + # web-ui form only allows one file, but technically it's possible to + # upload multiple files at the same time + for filename, f in request.files.items(): + buckets = json.loads(f.stream.read())["buckets"] + current_app.api.import_all(buckets) + # Normal import from body + else: + buckets = request.get_json()["buckets"] current_app.api.import_all(buckets) - # Normal import from body - else: - buckets = request.get_json()["buckets"] - current_app.api.import_all(buckets) - return None, 200 + except Exception as e: + logger.exception("Import failed") + return {"message": str(e)}, 400 + return {"message": "Import successful"}, 200 # LOGGING From 9210d8c281222756ec5c409bd793042e850f8e5f Mon Sep 17 00:00:00 2001 From: Bmans Date: Fri, 26 Jun 2026 16:12:20 -0400 Subject: [PATCH 2/2] fix(import): make multi-bucket import atomic with rollback on failure If import_bucket fails partway through a multi-bucket export, previously- committed buckets are now deleted before re-raising, so the database is never left in a partially-imported state. Co-Authored-By: Claude Sonnet 4.6 --- aw_server/api.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/aw_server/api.py b/aw_server/api.py index ddcdbc8..3c672ed 100644 --- a/aw_server/api.py +++ b/aw_server/api.py @@ -136,8 +136,15 @@ def import_bucket(self, bucket_data: Any): ) def import_all(self, buckets: Dict[str, Any]): - for bid, bucket in buckets.items(): - self.import_bucket(bucket) + imported: List[str] = [] + try: + for _bid, bucket in buckets.items(): + self.import_bucket(bucket) + imported.append(bucket["id"]) + except Exception: + for bid in imported: + self.db.delete_bucket(bid) + raise def create_bucket( self,