odysseus-dev/odysseus · warning · HTTPException
Album not found
Error message
Album not found
What it means
HTTP 404 from _get_or_404_album when no GalleryAlbum row exists with the given album_id in the database. This is the 'does not exist' branch; the separate owner check (609) is the 'exists but not yours' branch, deliberately returning the same status and message to avoid leaking album existence.
Source
Thrown at routes/gallery/gallery_routes.py:2127
enhanced = img.filter(ImageFilter.MedianFilter(size=3)) # light denoise
enhanced = enhanced.filter(ImageFilter.UnsharpMask(radius=2, percent=150, threshold=3)) # sharpen
enhanced = ImageEnhance.Contrast(enhanced).enhance(1.15) # slight contrast boost
enhanced = ImageEnhance.Color(enhanced).enhance(1.1) # subtle color boost
enhanced = ImageEnhance.Brightness(enhanced).enhance(1.05) # slight brightness lift
buf = io.BytesIO()
enhanced.save(buf, format="PNG")
return {"image": base64.b64encode(buf.getvalue()).decode(), "method": "pil"}
except Exception:
logger.exception("enhance_face: failed")
raise HTTPException(500, "Face enhancement failed")
# ---- Album management (path-param routes) ----
def _get_or_404_album(db, album_id: str, user):
album = db.query(GalleryAlbum).filter(GalleryAlbum.id == album_id).first()
if not album:
raise HTTPException(404, "Album not found")
if not user or album.owner != user:
raise HTTPException(404, "Album not found")
return album
def _get_or_404_image(db, image_id: str, user):
img = db.query(GalleryImage).filter(GalleryImage.id == image_id).first()
if not img:
raise HTTPException(404, "Image not found")
if not user or img.owner != user:
raise HTTPException(404, "Image not found")
return img
@router.put("/api/gallery/albums/{album_id}")
async def update_album(request: Request, album_id: str):
user = get_current_user(request)
data = await request.json()
db = SessionLocal()
try:View on GitHub (pinned to f9235ebbf1)
Solutions
- Re-fetch the album list (GET /api/gallery/albums) and use a current ID
- Treat 404 on album ops as 'refresh state' in the UI rather than an error dialog
- Confirm the album was not deleted by another session sharing the account
Example fix
# client
resp = requests.put(f'{base}/api/gallery/albums/{aid}', json=body)
if resp.status_code == 404:
albums = requests.get(f'{base}/api/gallery/albums').json() # refresh
aid = pick_album(albums) Defensive patterns
Strategy: validation
Validate before calling
def album_exists(album_id: str) -> bool:
albums = requests.get(f'{base}/api/gallery/albums', headers=hdrs, timeout=30).json()
return any(a.get('id') == album_id for a in albums.get('albums', albums)) Try / catch
try:
update_album(album_id, name='Holiday')
except HTTPError as e:
if e.response.status_code == 404:
albums = refresh_album_cache()
album_id = first(a['id'] for a in albums if a['name'] == 'Holiday') or create_album('Holiday')
update_album(album_id, name='Holiday')
else:
raise Prevention
- Refresh the album list after any delete event or tab focus change before issuing mutations
- Treat 404 on album routes as a UI state-refresh trigger, not a hard error
- Note that IDs are UUID-like strings — validate format client-side to catch truncation
When it happens
Trigger: PUT/DELETE /api/gallery/albums/{album_id} (and image-in-album ops) with an ID from a deleted album, a stale client cache, or a typo/truncated UUID.
Common situations: Album deleted in another tab or by another session; client holds an old album list; database reset/re-migrated so IDs no longer resolve.
Related errors
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/741355a4e2f7894c.
Report an issue: GitHub.