odysseus-dev/odysseus · error · HTTPException
Image file not found
Error message
Image file not found
What it means
HTTP 404 raised by POST /api/gallery/{image_id}/rotate when the DB row exists but the backing file is missing on disk (img_path.exists() is false). This indicates DB/filesystem desync: the record survived while the file under GALLERY_IMAGE_DIR was deleted, moved, or never written.
Source
Thrown at routes/gallery/gallery_routes.py:530
try:
angle = int(data.get("angle", 90))
except (TypeError, ValueError):
raise HTTPException(400, "Invalid angle")
if angle not in (90, -90, 180, 270):
raise HTTPException(400, "Angle must be 90, -90, 180, or 270")
user = get_current_user(request)
db = SessionLocal()
try:
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(403, "Not your image")
img_path = _gallery_image_path(img.filename)
if not img_path.exists():
raise HTTPException(404, "Image file not found")
# PIL rotates counter-clockwise; the API takes "clockwise"
# convention so we negate to match user expectation.
with Image.open(img_path) as pil:
rotated = pil.rotate(-angle, expand=True)
# Recompute hash so dedupe stays accurate.
buf = BytesIO()
ext = img.filename.rsplit(".", 1)[-1].lower()
save_kwargs = {}
if ext in ("jpg", "jpeg"):
save_kwargs["quality"] = 95
fmt = "JPEG"
elif ext == "webp":
fmt = "WEBP"
save_kwargs["quality"] = 95
else:
fmt = "PNG"
rotated.save(buf, format=fmt, **save_kwargs)View on GitHub (pinned to f9235ebbf1)
Solutions
- Check GALLERY_IMAGE_DIR for the expected filename from img.filename.
- Restore the file from backup, or delete the orphan DB row so the gallery stays consistent.
- Mount the gallery directory on persistent storage in containerized deployments.
Defensive patterns
Strategy: try-catch
Try / catch
try { await rotateImage(id, angle); } catch (e) {
if (e.status === 404) {
const meta = await getImage(id);
if (meta) reportDesync(id, 'DB row exists but file missing'); // ops alert
}
} Prevention
- Persist the gallery image directory (volumes in containers)
- Exclude gallery dirs from cleanup jobs
- Restore DB and files together from backup
- Reconcile orphan rows periodically
When it happens
Trigger: Manual cleanup of the gallery image directory; a crashed write in a previous operation; volume/container restarts losing ephemeral storage; the filename column containing a stale path.
Common situations: Docker deployments without persistent volumes; rsync/cron jobs pruning 'orphan' files; partial restore from backup that recovered the DB but not the files.
Related errors
- Invalid angle
- Angle must be 90, -90, 180, or 270
- HTTP ${resp.status}${detail ? `: ${detail}` : ''}
- HTTP ${saveRes.status}: ${errBody.substring(0, 120)}
- HTTP ${res.status}
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/62630122458e5922.
Report an issue: GitHub.