jamiepine/voicebox · info · HTTPException

No avatar found for this profile

Error message

No avatar found for this profile

What it means

GET /profiles/{profile_id}/avatar reaches this branch only when the profile exists but profile.avatar_path is falsy (None/empty). The route raises 404 'No avatar found for this profile'. This is distinct from 'Profile not found' (id missing) and 'Avatar file not found' (avatar_path set but file gone): here the profile simply never had an avatar uploaded, or it was previously deleted.

Source

Thrown at backend/routes/profiles.py:260

        return profile
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    finally:
        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"}

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Treat this 404 as 'use default avatar' on the client -- it is a normal state, not an error.
  2. If an avatar is expected, upload one via POST /profiles/{profile_id}/avatar first.
  3. Distinguish the two 404 detail strings ('No avatar found' vs 'Avatar file not found') to drive different UX.
Defensive patterns

Strategy: fallback

Validate before calling

async def avatar_url_or_default(client, profile_id, fallback):
    profile = (await client.get(f"/profiles/{profile_id}")).json()
    if not profile.get("avatar_path"):
        return fallback
    return f"/profiles/{profile_id}/avatar"

Type guard

def has_avatar(profile_dict: dict) -> bool:
    """True only when the profile actually has an avatar path set."""
    return bool(profile_dict.get("avatar_path"))

Try / catch

resp = await client.get(f"/profiles/{profile_id}/avatar")
if resp.status_code == 404 and resp.json()["detail"] == "No avatar found for this profile":
    use_default_avatar()       # normal state, not an error
else:
    resp.raise_for_status()

Prevention

When it happens

Trigger: Calling the avatar endpoint on a freshly created profile before any upload; after DELETE /profiles/{id}/avatar succeeded; on a profile imported from a ZIP that did not include an avatar.

Common situations: UI always calls /avatar for every profile and expects some to have none; import flow that omits avatar blobs.

Related errors


AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12). Data as JSON: /api/errors/c3303bc72b3afb1d. Report an issue: GitHub.