{"record":{"id":"aca33f146c69ab2a","repo":"jamiepine/voicebox","slug":"no-audio-file-found-in-zip-archive","errorCode":null,"errorMessage":"No audio file found in ZIP archive","messagePattern":"No audio file found in ZIP archive","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/services/export_import.py","lineNumber":384,"sourceCode":"            if \"version\" not in manifest_data:\n                raise ValueError(\"Invalid manifest.json: missing version\")\n            \n            if \"generation\" not in manifest_data:\n                raise ValueError(\"Invalid manifest.json: missing generation data\")\n            \n            generation_data = manifest_data[\"generation\"]\n            profile_data = manifest_data.get(\"profile\", {})\n            \n            # Validate required fields\n            required_fields = [\"text\", \"language\", \"duration\"]\n            for field in required_fields:\n                if field not in generation_data:\n                    raise ValueError(f\"Invalid manifest.json: missing generation.{field}\")\n            \n            # Find audio file in archive\n            audio_files = [f for f in namelist if f.startswith(\"audio/\") and f.endswith(\".wav\")]\n            if not audio_files:\n                raise ValueError(\"No audio file found in ZIP archive\")\n            \n            audio_file_path = audio_files[0]\n            \n            # Check if we should match an existing profile or create metadata\n            profile_id = None\n            profile_name = profile_data.get(\"name\", \"Unknown Profile\")\n            \n            # Try to find matching profile by name\n            if profile_name and profile_name != \"Unknown Profile\":\n                existing_profile = db.query(DBVoiceProfile).filter_by(name=profile_name).first()\n                if existing_profile:\n                    profile_id = existing_profile.id\n            \n            # If no matching profile, use a placeholder or the first available profile\n            if not profile_id:\n                # Get any profile, or None if no profiles exist\n                any_profile = db.query(DBVoiceProfile).first()\n                if any_profile:","sourceCodeStart":366,"sourceCodeEnd":402,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/services/export_import.py#L366-L402","documentation":"Raised during generation import when no entry in the ZIP namelist both starts with 'audio/' and ends with '.wav' (export_import.py:382-384). The manifest passed validation but the audio payload the manifest describes is absent or named differently. The importer takes audio_files[0], so an empty list is fatal.","triggerScenarios":"Archive was rebuilt after editing and the audio entry was placed at the root instead of under audio/; the audio was renamed to .mp3 or omitted; a partial export wrote the manifest before the audio and then crashed.","commonSituations":"Re-zipping by hand and losing the audio/ prefix; exporter bug that wrote a different path; format conversion stripped the .wav extension.","solutions":["Unzip the archive and confirm a file exists at exactly audio/<something>.wav (case-sensitive on Linux).","If the audio is elsewhere, move it under audio/ and re-zip preserving paths.","Re-export from the source instance.","If the source audio is .mp3, convert to .wav before importing — this importer accepts WAV only."],"exampleFix":"# before: audio at archive root\nzip -r gen.zip manifest.json clip.wav\n# after: audio under audio/\nmkdir audio && mv clip.wav audio/clip.wav\nzip -r gen.zip manifest.json audio/clip.wav","handlingStrategy":"validation","validationCode":"import zipfile, io\n\ndef archive_has_wav_audio(file_bytes: bytes) -> bool:\n    with zipfile.ZipFile(io.BytesIO(file_bytes)) as z:\n        return any(n.startswith('audio/') and n.endswith('.wav') for n in z.namelist())","typeGuard":"null","tryCatchPattern":"try:\n    result = await import_generation_from_bytes(file_bytes, db)\nexcept ValueError as e:\n    if str(e) == 'No audio file found in ZIP archive':\n        raise HTTPException(400, 'Archive has no audio/*.wav entry; re-export with the audio included.')\n    raise HTTPException(400, str(e))","preventionTips":["Always re-zip with the audio/ prefix intact.","When rebuilding archives, use `zip -r` from a directory whose structure matches export_generation_to_zip.","Accept only WAV for this importer — convert formats upstream."],"tags":["zip","import","audio","validation","generation"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}