jamiepine/voicebox · warning · HTTPException
Profile not found or no avatar to delete
Error message
Profile not found or no avatar to delete
What it means
DELETE /profiles/{profile_id}/avatar calls profiles.delete_avatar; if it returns falsy the route raises 404 'Profile not found or no avatar to delete'. The service collapses two distinct conditions into one False return -- the profile_id does not exist, or the profile exists but has no avatar_path to clear. Callers cannot tell which from the response alone.
Source
Thrown at backend/routes/profiles.py:277
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(
profile_id: str,
db: Session = Depends(get_db),
):
"""Export a voice profile as a ZIP archive."""
try:
profile = await profiles.get_profile(profile_id, db)
if not profile:
raise HTTPException(status_code=404, detail="Profile not found")
zip_bytes = export_import.export_profile_to_zip(profile_id, db)
safe_name = "".join(c for c in profile.name if c.isalnum() or c in (" ", "-", "_")).strip()
if not safe_name:View on GitHub (pinned to 51f49dea19)
Solutions
- Treat the 404 as success for idempotent avatar deletion (end state: no avatar).
- If you need to distinguish the two cases, GET /profiles/{profile_id} first and inspect avatar_path.
- Guard the UI delete button so it only appears when an avatar is known to exist.
Defensive patterns
Strategy: try-catch
Validate before calling
async def avatar_exists(client, profile_id) -> bool:
profile = (await client.get(f"/profiles/{profile_id}")).json()
return bool(profile.get("avatar_path")) Try / catch
resp = await client.delete(f"/profiles/{profile_id}/avatar")
if resp.status_code == 404:
# Either profile is gone or there was no avatar -- both are acceptable end states
pass
else:
resp.raise_for_status()
refresh_profile(profile_id) Prevention
- Treat this 404 as success for idempotent avatar removal.
- Only show the 'remove avatar' control when avatar_path is set.
- Refresh the profile after delete to update the UI.
When it happens
Trigger: Calling delete on a profile that never had an avatar uploaded; calling delete on a profile_id that was already removed; double-deleting after a successful first delete (the second sees avatar_path already None).
Common situations: Frontend always shows a 'remove avatar' button regardless of whether one is set; user clicks remove twice; sync script re-runs.
Related errors
- Sample not found
- No avatar found for this profile
- Avatar file not found
- Generation failed; no audio available
- Sample not found
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/a92bfbfd6fc6ee1b.
Report an issue: GitHub.