Skip to content

refactor(albums): improve SQLite error handling and logging - #1414

Open
Dotify71 wants to merge 11 commits into
AOSSIE-Org:mainfrom
Dotify71:fix/albums-error-handling
Open

refactor(albums): improve SQLite error handling and logging#1414
Dotify71 wants to merge 11 commits into
AOSSIE-Org:mainfrom
Dotify71:fix/albums-error-handling

Conversation

@Dotify71

@Dotify71 Dotify71 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Description

This PR addresses issue #1340 by refactoring the SQLite error handling and integrating the standard logger within the albums database layer.

Changes made:

  1. Replaced generic sqlite3.Error catching with specific exceptions (sqlite3.IntegrityError, sqlite3.OperationalError, etc.) in backend/app/database/albums.py.
  2. Integrated backend/app/utils/logger.py to appropriately log database errors, ensuring better traceability and consistency with the rest of the database modules.

Closes #1340

Checklist

  • Code follows the project's contributing guidelines.
  • Tested changes locally.

Summary by CodeRabbit

  • Bug Fixes
    • Standardized album/image endpoint error handling so unexpected failures consistently return 500.
    • Album creation and updates now return 409 Conflict for integrity/duplicate violations.
    • Adding images returns 400 Bad Request for invalid input; removing images returns 404 Not Found when the image isn’t in the album (with the mapped message).
  • Reliability
    • Improved SQLite error logging for album database operations, including password verification.
  • Tests
    • Added coverage ensuring unexpected backend exceptions are mapped to 500.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Album database helpers now use logged connection contexts for SQLite operations. Album routes centralize unexpected-exception handling while preserving explicit status mappings, and tests update database patching, bcrypt inputs, type annotations, and HTTP 500 coverage.

Changes

Album Error Handling

Layer / File(s) Summary
Database connection and operations
backend/app/database/albums.py
Schema creation, album operations, image associations, and password verification use logged SQLite connection contexts with updated validation and type annotations.
Route exception boundaries
backend/app/routes/albums.py
Routes use a shared exception decorator and retain specific mappings for conflicts, validation errors, missing resources, and unexpected failures.
Test fixtures and error coverage
backend/tests/test_albums.py, backend/tests/test_albums_db.py
Tests update bcrypt inputs and database patch targets, modernize helper annotations, and verify unexpected route failures return HTTP 500 responses.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AlbumRoute
  participant AlbumDatabase
  Client->>AlbumRoute: send album or image request
  AlbumRoute->>AlbumDatabase: invoke database helper
  AlbumDatabase-->>AlbumRoute: return data or raise exception
  AlbumRoute-->>Client: return structured HTTP response
Loading

Suggested labels: Python

Poem

I’m a rabbit with logs in my den,
SQLite errors now speak up again.
Albums hop through safer connection flow,
Routes keep their proper errors in tow.
Tests nibble the bugs away!

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also adds an album_images(image_id) index and broader route/test refactors that are not part of the logged error-handling scope. Remove or justify the extra schema/index and unrelated refactors, keeping the PR focused on SQLite error handling and logging.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: refactoring album SQLite error handling and logging.
Linked Issues check ✅ Passed The database and route changes add SQLite error logging, specific exception handling, and HTTPException passthrough as requested for #1340.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
backend/app/database/albums.py (2)

7-8: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove redundant logger comments.

  • backend/app/database/albums.py#L7-L8: remove # Initialize logger.
  • backend/app/routes/albums.py#L31-L32: remove # Initialize logger.

As per path instructions, “Point out redundant obvious comments that do not add clarity.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/database/albums.py` around lines 7 - 8, Remove the redundant
“Initialize logger” comments above the logger declarations in
backend/app/database/albums.py lines 7-8 and backend/app/routes/albums.py lines
31-32; leave the logger declarations unchanged.

Source: Path instructions


51-86: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add concrete return annotations to the album lookup helpers.

get_db_connection() returns a default SQLite connection, so db_get_all_albums() returns list[tuple], while db_get_album_by_name() and db_get_album() return tuple | None for the albums row shape. Annotate these explicitly instead of returning bare fetchall()/fetchone() rows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/database/albums.py` around lines 51 - 86, The album lookup
helpers lack explicit return types. Update db_get_all_albums to return
list[tuple], and annotate db_get_album_by_name and db_get_album as returning
tuple | None, preserving their existing query and result behavior.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/app/database/albums.py`:
- Around line 26-28: Add automated route-level tests around the albums handlers
in backend/app/routes/albums.py (lines 53-64) covering unexpected exceptions
returning structured 500 responses and explicit HTTPException cases such as 401,
404, and 409 preserving their original status and detail; retain the existing
sqlite3.Error cleanup behavior in backend/app/database/albums.py (lines 26-28),
which requires no direct change.

In `@backend/app/routes/albums.py`:
- Around line 55-64: Stop exposing str(e) in the ErrorResponse messages for all
listed exception handlers in backend/app/routes/albums.py:55-64, 87-90, 121-124,
183-186, 212-215, 274-277, 315-318, 346-349, and 389-392. Keep the detailed
exceptions in logger.error, but replace each client-facing message with its
fixed operation-specific fetch, create, update, delete, image-fetch, image-add,
image-removal, or bulk-removal failure message.

---

Nitpick comments:
In `@backend/app/database/albums.py`:
- Around line 7-8: Remove the redundant “Initialize logger” comments above the
logger declarations in backend/app/database/albums.py lines 7-8 and
backend/app/routes/albums.py lines 31-32; leave the logger declarations
unchanged.
- Around line 51-86: The album lookup helpers lack explicit return types. Update
db_get_all_albums to return list[tuple], and annotate db_get_album_by_name and
db_get_album as returning tuple | None, preserving their existing query and
result behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 48121a55-f156-48da-88ab-10ab0b70fff7

📥 Commits

Reviewing files that changed from the base of the PR and between 823a7a4 and e582865.

📒 Files selected for processing (2)
  • backend/app/database/albums.py
  • backend/app/routes/albums.py

Comment thread backend/app/routes/albums.py Outdated
@Dotify71
Dotify71 force-pushed the fix/albums-error-handling branch from e582865 to d41adb7 Compare July 24, 2026 18:43

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (2)
backend/app/database/albums.py (1)

11-256: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting the repeated try/except/log/raise boilerplate.

All 13 functions repeat the identical try: ... except sqlite3.Error as e: logger.error(...); raise shape, differing only in the log message. A small decorator or a context manager wrapping get_db_connection() with logging could remove this duplication and make it trivial to apply the specific-exception fix above consistently.

♻️ Sketch
from contextlib import contextmanager

`@contextmanager`
def logged_db_connection(action: str):
    try:
        with get_db_connection() as conn:
            yield conn
    except sqlite3.Error as e:
        logger.error(f"Error {action}: {e}")
        raise
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/database/albums.py` around lines 11 - 256, Extract the repeated
SQLite error-handling in all database functions into a shared helper, preferably
a context manager wrapping get_db_connection() that accepts the
operation-specific action and logs sqlite3.Error before re-raising. Update
db_create_albums_table, db_create_album_images_table, db_get_all_albums,
db_get_album_by_name, db_get_album, db_insert_album, db_update_album,
db_delete_album, db_get_album_images, db_add_images_to_album,
db_remove_image_from_album, db_remove_images_from_album, and
verify_album_password to use it while preserving their existing contextual log
messages.
backend/app/routes/albums.py (1)

41-65: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Repeated except HTTPException: raise / except Exception: ... 500 boilerplate across 9 handlers.

Every handler repeats the same two-clause exception boundary. A small decorator/dependency wrapping route bodies (log + convert to the appropriate ErrorResponse) would remove this duplication and make it easier to enforce the exception-type distinctions raised above consistently.

Also applies to: 71-99, 105-133, 141-197, 203-228, 239-292, 300-337, 343-370, 378-414

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/routes/albums.py` around lines 41 - 65, Introduce a shared route
exception wrapper for get_albums and the other affected handlers that re-raises
HTTPException unchanged, logs unexpected exceptions, and converts them to the
existing 500 ErrorResponse structure. Apply the wrapper consistently to all nine
handlers and remove their duplicated try/except boundaries, preserving each
route’s existing success behavior and error message context.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/app/database/albums.py`:
- Around line 24-26: Replace the generic sqlite3.Error handlers throughout this
file with specific sqlite3.IntegrityError and sqlite3.OperationalError handling,
including the album insertion flow in db_insert_album and every other listed
database operation. Preserve the existing logging and re-raise behavior while
ensuring UNIQUE/constraint violations remain distinguishable from operational
database failures.
- Around line 1-256: Add `from __future__ import annotations` at the beginning
of the albums database module, before imports and before signatures such as
`db_get_album_by_name`, `db_get_album`, `db_insert_album`, and `db_update_album`
that use PEP 604 annotations; leave the existing function behavior unchanged.

In `@backend/app/routes/albums.py`:
- Around line 295-338: Update add_images_to_album to catch the ValueError and
TypeError raised by db_add_images_to_album for invalid or nonexistent image IDs
and translate them into a 400 HTTPException using the route’s existing
ErrorResponse structure; leave unexpected exceptions on the generic 500 path.
- Around line 340-371: Update remove_image_from_album to catch the ValueError
raised by db_remove_image_from_album for a missing image and translate it into
the existing 404 response pattern, while preserving the generic 500 handling for
unexpected exceptions.
- Around line 68-100: Update create_album to catch the sqlite3.IntegrityError
raised by db_insert_album when the unique album-name constraint loses a
concurrent race, and map that case to the same 409 Conflict response used for an
existing album. Preserve the generic 500 handling for unrelated exceptions and
retain the existing pre-insert duplicate check.
- Around line 136-198: Update the unexpected-exception fallback in update_album
to raise HTTP_500_INTERNAL_SERVER_ERROR instead of HTTP_400_BAD_REQUEST, while
preserving the existing HTTPException handling, logging, and error response.

---

Nitpick comments:
In `@backend/app/database/albums.py`:
- Around line 11-256: Extract the repeated SQLite error-handling in all database
functions into a shared helper, preferably a context manager wrapping
get_db_connection() that accepts the operation-specific action and logs
sqlite3.Error before re-raising. Update db_create_albums_table,
db_create_album_images_table, db_get_all_albums, db_get_album_by_name,
db_get_album, db_insert_album, db_update_album, db_delete_album,
db_get_album_images, db_add_images_to_album, db_remove_image_from_album,
db_remove_images_from_album, and verify_album_password to use it while
preserving their existing contextual log messages.

In `@backend/app/routes/albums.py`:
- Around line 41-65: Introduce a shared route exception wrapper for get_albums
and the other affected handlers that re-raises HTTPException unchanged, logs
unexpected exceptions, and converts them to the existing 500 ErrorResponse
structure. Apply the wrapper consistently to all nine handlers and remove their
duplicated try/except boundaries, preserving each route’s existing success
behavior and error message context.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b0588f46-07df-42cc-884b-4d683dc91d38

📥 Commits

Reviewing files that changed from the base of the PR and between e582865 and d41adb7.

📒 Files selected for processing (2)
  • backend/app/database/albums.py
  • backend/app/routes/albums.py

Comment thread backend/app/database/albums.py Outdated
Comment thread backend/app/database/albums.py
Comment thread backend/app/routes/albums.py
Comment thread backend/app/routes/albums.py
Comment thread backend/app/routes/albums.py Outdated
Comment thread backend/app/routes/albums.py
@Dotify71
Dotify71 force-pushed the fix/albums-error-handling branch from d41adb7 to e564fdb Compare July 24, 2026 18:53

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
backend/app/routes/albums.py (1)

38-59: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add type annotations to the decorator factory.

The new factory, nested decorator, and wrapper are untyped. Annotate the callable contract while preserving the wrapped route signature.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/routes/albums.py` around lines 38 - 59, The
handle_route_exceptions decorator factory and its nested decorator/wrapper are
missing callable type annotations. Add appropriate generic ParamSpec and TypeVar
annotations to decorator, func, args, kwargs, and the returned callable so the
wrapped route’s signature is preserved.

Source: Path instructions

backend/tests/test_albums.py (1)

477-490: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the database-enforced duplicate-create path.

This only tests generic 500 conversion. Add a test where db_get_album_by_name returns None and db_insert_album raises sqlite3.IntegrityError, then assert the route returns 409. The existing duplicate test exits before insertion.

As per path instructions, ensure critical functionality is covered by automated tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_albums.py` around lines 477 - 490, Extend
TestAlbumRouteErrors with a test for the database-enforced duplicate-create
path: patch db_get_album_by_name to return None, make db_insert_album raise
sqlite3.IntegrityError, call the album creation route with valid input, and
assert the response status is 409. Keep the existing generic exception test
unchanged.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/app/routes/albums.py`:
- Around line 201-204: Update the album update handler around db_update_album to
catch sqlite3.IntegrityError caused by a duplicate album name and return the
same 409 response used by create_album, instead of allowing the decorator to
convert it to 500. Preserve the existing successful update response for
non-conflicting updates.

---

Nitpick comments:
In `@backend/app/routes/albums.py`:
- Around line 38-59: The handle_route_exceptions decorator factory and its
nested decorator/wrapper are missing callable type annotations. Add appropriate
generic ParamSpec and TypeVar annotations to decorator, func, args, kwargs, and
the returned callable so the wrapped route’s signature is preserved.

In `@backend/tests/test_albums.py`:
- Around line 477-490: Extend TestAlbumRouteErrors with a test for the
database-enforced duplicate-create path: patch db_get_album_by_name to return
None, make db_insert_album raise sqlite3.IntegrityError, call the album creation
route with valid input, and assert the response status is 409. Keep the existing
generic exception test unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d1df77d6-c282-4c18-87e3-183c93465509

📥 Commits

Reviewing files that changed from the base of the PR and between d41adb7 and 86271c9.

📒 Files selected for processing (4)
  • backend/app/database/albums.py
  • backend/app/routes/albums.py
  • backend/tests/test_albums.py
  • backend/tests/test_albums_db.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/app/database/albums.py

Comment thread backend/app/routes/albums.py Outdated
@Dotify71

Dotify71 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Everything is set and the pr is ready to review or merge. :))

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ This PR has merge conflicts.

Please resolve the merge conflicts before review.

Your PR will only be reviewed by a maintainer after all conflicts have been resolved.

📺 Watch this video to understand why conflicts occur and how to resolve them:
https://www.youtube.com/watch?v=Sqsz1-o7nXk

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor: Improve SQLite error handling and integrate logger in albums database layer

1 participant