fix(import): return descriptive error messages and success response#165
fix(import): return descriptive error messages and success response#165behdadmansouri wants to merge 1 commit into
Conversation
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 <noreply@anthropic.com>
Greptile SummaryThis PR improves the
Confidence Score: 3/5Safe for single-bucket imports; multi-bucket imports can leave the database in a partially-written state when a later bucket triggers the new duplicate check. The new duplicate-bucket check in aw_server/api.py — specifically the Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Client
participant REST as rest.py (ImportAllResource.post)
participant API as api.py (ServerAPI.import_all)
participant DB as Database
Client->>REST: POST /0/import (file or JSON body)
REST->>REST: try block entered
alt File upload
REST->>REST: parse JSON from file stream
else JSON body
REST->>REST: request.get_json()["buckets"]
end
REST->>API: import_all(buckets)
loop For each bucket
API->>DB: db.buckets() — check existence
alt Bucket already exists
API-->>REST: raise Exception("Bucket '...' already exists")
REST-->>Client: "400 {"message": "Bucket '...' already exists..."}"
else New bucket
API->>DB: create_bucket(...)
API->>DB: create_events(...)
end
end
REST-->>Client: "200 {"message": "Import successful"}"
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Client
participant REST as rest.py (ImportAllResource.post)
participant API as api.py (ServerAPI.import_all)
participant DB as Database
Client->>REST: POST /0/import (file or JSON body)
REST->>REST: try block entered
alt File upload
REST->>REST: parse JSON from file stream
else JSON body
REST->>REST: request.get_json()["buckets"]
end
REST->>API: import_all(buckets)
loop For each bucket
API->>DB: db.buckets() — check existence
alt Bucket already exists
API-->>REST: raise Exception("Bucket '...' already exists")
REST-->>Client: "400 {"message": "Bucket '...' already exists..."}"
else New bucket
API->>DB: create_bucket(...)
API->>DB: create_events(...)
end
end
REST-->>Client: "200 {"message": "Import successful"}"
|
| if bucket_id in self.db.buckets(): | ||
| raise Exception( | ||
| f"Bucket '{bucket_id}' already exists. Delete it first or rename the bucket before importing." | ||
| ) |
There was a problem hiding this comment.
Raising a bare
Exception means the caller cannot distinguish this user-facing validation error from unexpected runtime errors. A more specific type (e.g., ValueError) lets rest.py selectively catch expected errors and log them at WARNING level rather than ERROR, while letting unexpected exceptions propagate differently if needed in the future.
| if bucket_id in self.db.buckets(): | |
| raise Exception( | |
| f"Bucket '{bucket_id}' already exists. Delete it first or rename the bucket before importing." | |
| ) | |
| if bucket_id in self.db.buckets(): | |
| raise ValueError( | |
| f"Bucket '{bucket_id}' already exists. Delete it first or rename the bucket before importing." | |
| ) |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| except Exception as e: | ||
| logger.exception("Import failed") | ||
| return {"message": str(e)}, 400 |
There was a problem hiding this comment.
logger.exception emits a full traceback. For the expected "bucket already exists" case this is noise — it's a user-facing validation error, not an unexpected server error. Logging it at WARNING (without the traceback) avoids polluting the log with stack traces for routine user mistakes, while keeping real surprises at ERROR/EXCEPTION level.
| except Exception as e: | |
| logger.exception("Import failed") | |
| return {"message": str(e)}, 400 | |
| except Exception as e: | |
| logger.warning("Import failed: %s", e) | |
| return {"message": str(e)}, 400 |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Summary
Fixes ActivityWatch/activitywatch#394
# TODO: Check that bucket doesn't already existcomment){"message": "..."}body instead of a generic 500; return{"message": "Import successful"}on success instead ofnullTest plan
{"message": "Import successful"}with 200{"message": "Bucket '...' already exists. Delete it first..."}with 400