{"record":{"id":"b845b8f582763b98","repo":"jamiepine/voicebox","slug":"profile-profile-id-not-found-b845b8","errorCode":null,"errorMessage":"Profile {profile_id} not found","messagePattern":"Profile (.+?) not found","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/services/export_import.py","lineNumber":62,"sourceCode":"\ndef export_profile_to_zip(profile_id: str, db: Session) -> bytes:\n    \"\"\"\n    Export a voice profile to a ZIP archive.\n    \n    Args:\n        profile_id: Profile ID to export\n        db: Database session\n        \n    Returns:\n        ZIP file contents as bytes\n        \n    Raises:\n        ValueError: If profile not found or has no samples\n    \"\"\"\n    # Get profile\n    profile = db.query(DBVoiceProfile).filter_by(id=profile_id).first()\n    if not profile:\n        raise ValueError(f\"Profile {profile_id} not found\")\n    \n    # Get all samples\n    samples = db.query(DBProfileSample).filter_by(profile_id=profile_id).all()\n    if not samples:\n        raise ValueError(f\"Profile {profile_id} has no samples\")\n    \n    # Create ZIP in memory\n    zip_buffer = io.BytesIO()\n    \n    with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:\n        # Check if profile has avatar\n        has_avatar = False\n        if profile.avatar_path:\n            avatar_path = config.resolve_storage_path(profile.avatar_path)\n            if avatar_path is not None and avatar_path.exists():\n                has_avatar = True\n                # Add avatar to ZIP root with original extension\n                avatar_ext = avatar_path.suffix","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/services/export_import.py#L44-L80","documentation":"export_profile_to_zip() looks up DBVoiceProfile by id; if no row matches it raises ValueError. The profile must exist before its samples can be archived.","triggerScenarios":"Calling the profile-export endpoint with a profile_id that was deleted, never existed, or came from a different database (e.g. after a DB reset/migration).","commonSituations":"Stale bookmarked export link; profile_id typo; post-migration missing rows; client holding a cached id after the profile was deleted elsewhere.","solutions":["Verify the profile_id exists before offering the Export action.","Map this ValueError to 404 at the API layer (not 500).","Refresh the profile list in the UI when the row is missing."],"exampleFix":"# before\ndata = export_profile_to_zip(unknown_id, db)\n\n# after\nif not db.query(DBVoiceProfile).filter_by(id=pid).first():\n    return None  # API returns 404\ndata = export_profile_to_zip(pid, db)","handlingStrategy":"validation","validationCode":"from backend.database import VoiceProfile as DBVoiceProfile\n\ndef profile_exists(profile_id, db) -> bool:\n    return db.query(DBVoiceProfile).filter_by(id=profile_id).first() is not None","typeGuard":null,"tryCatchPattern":"try:\n    data = export_profile_to_zip(profile_id, db)\nexcept ValueError as e:\n    if \"not found\" in str(e):\n        return (\"not_found\", None)  # API maps to 404\n    raise","preventionTips":["Check existence before exposing the Export action.","Return 404 (not 500) when this ValueError surfaces at the API."],"tags":["profiles","export","not-found","validation"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}