jamiepine/voicebox · error · HTTPException
Avatar file not found
Error message
Avatar file not found
What it means
GET /profiles/{profile_id}/avatar reaches this branch when profile.avatar_path is set, but config.resolve_storage_path returns None (storage root misconfigured/unresolvable) or the resolved Path does not exist on disk. The route raises 404 'Avatar file not found'. This indicates storage drift: the DB row references a blob that is no longer present, or the storage configuration itself is broken so the path cannot be resolved at all.
Source
Thrown at backend/routes/profiles.py:264
Path(tmp_path).unlink(missing_ok=True)
@router.get("/profiles/{profile_id}/avatar")
async def get_profile_avatar(
profile_id: str,
db: Session = Depends(get_db),
):
"""Get avatar image for a profile."""
profile = await profiles.get_profile(profile_id, db)
if not profile:
raise HTTPException(status_code=404, detail="Profile not found")
if not profile.avatar_path:
raise HTTPException(status_code=404, detail="No avatar found for this profile")
avatar_path = config.resolve_storage_path(profile.avatar_path)
if avatar_path is None or not avatar_path.exists():
raise HTTPException(status_code=404, detail="Avatar file not found")
return FileResponse(avatar_path)
@router.delete("/profiles/{profile_id}/avatar")
async def delete_profile_avatar(
profile_id: str,
db: Session = Depends(get_db),
):
"""Delete avatar image for a profile."""
success = await profiles.delete_avatar(profile_id, db)
if not success:
raise HTTPException(status_code=404, detail="Profile not found or no avatar to delete")
return {"message": "Avatar deleted successfully"}
@router.get("/profiles/{profile_id}/export")
async def export_profile(View on GitHub (pinned to 51f49dea19)
Solutions
- Verify the storage root: print config.resolve_storage_path(profile.avatar_path) server-side and check it points where you expect.
- Restore the missing blob from backup, or clear avatar_path in the DB so the profile reports 'No avatar found' instead.
- Mount a persistent volume at the configured storage path and redeploy.
- Audit the storage dir against the avatar_path column to find other drifted rows.
Defensive patterns
Strategy: try-catch
Validate before calling
# Reconcile storage before relying on the avatar endpoint
import requests
def storage_path_resolves(profile_avatar_path, storage_root):
candidate = (storage_root / profile_avatar_path) if profile_avatar_path else None
return candidate is not None and candidate.exists() Try / catch
resp = await client.get(f"/profiles/{profile_id}/avatar")
if resp.status_code == 404:
detail = resp.json()["detail"]
if detail == "Avatar file not found":
alert_admin(f"storage drift on profile {profile_id}")
# fall back to default and clear the stale avatar_path server-side
use_default_avatar()
else:
resp.raise_for_status() Prevention
- Mount persistent storage at the configured storage root in every environment.
- Run a periodic reconciliation of avatar_path rows against the blob directory.
- When restoring DB from backup, restore the blob dir in the same step.
When it happens
Trigger: STORAGE_DIR / VOICEBOX_STORAGE env var changed since the avatar was uploaded; files manually deleted from the storage volume; container restarted with ephemeral storage so uploaded blobs are gone; backup restored the DB without restoring the blob directory.
Common situations: Docker deployment without a persistent volume mounted at the storage path; migration to a new host that copied the DB but not the media dir; cron cleanup that aggressively removed 'old' files.
Related errors
- Generation failed; no audio available
- Audio file not found
- Source audio file not found
- Audio file not found
- No avatar found for this profile
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/6ffaf7582b9cd5a4.
Report an issue: GitHub.