jamiepine/voicebox · error · ValueError

Invalid ZIP file

Error message

Invalid ZIP file

What it means

Raised by the profile import path when Python's zipfile module emits zipfile.BadZipFile while opening the uploaded bytes (export_import.py:235). The bytes are not a valid ZIP container — truncated, wrong format, or zero-length. The handler normalizes the low-level BadZipFile into a ValueError("Invalid ZIP file") so callers see a single error type.

Source

Thrown at backend/services/export_import.py:236

                    tmp.write(zip_file.read(zip_path))
                    tmp_path = tmp.name
                
                try:
                    # Add sample to profile
                    await add_profile_sample(
                        profile.id,
                        tmp_path,
                        reference_text,
                        db,
                    )
                finally:
                    # Clean up temp file
                    Path(tmp_path).unlink(missing_ok=True)
            
            return profile
            
    except zipfile.BadZipFile:
        raise ValueError("Invalid ZIP file")
    except json.JSONDecodeError as e:
        raise ValueError(f"Invalid JSON in archive: {e}")
    except Exception as e:
        if isinstance(e, ValueError):
            raise
        raise ValueError(f"Error importing profile: {str(e)}")


def export_generation_to_zip(generation_id: str, db: Session) -> bytes:
    """
    Export a generation to a ZIP archive.
    
    Args:
        generation_id: Generation ID to export
        db: Database session
        
    Returns:
        ZIP file contents as bytes

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Verify the file magic bytes locally before upload: the first four bytes of a ZIP are b'PK\x03\x04'.
  2. Ensure the client sends the exact bytes produced by export_profile_to_zip without re-encoding (no base64, no form-wrapping unless the endpoint expects it).
  3. Re-export the profile on the source instance and retry; if it still fails, the export itself is the bug.
  4. Confirm you are hitting the profile import endpoint, not the generation import endpoint — they expect different manifests.

Example fix

// before (js client): sending a tarball
const body = new FormData(); body.append('file', tarballFile);
// after: validate magic bytes before sending
const buf = new Uint8Array(await file.arrayBuffer());
const isZip = buf[0]===0x50 && buf[1]===0x4b && buf[2]===0x03 && buf[3]===0x04;
if (!isZip) throw new Error('not a zip');
body.append('file', file);
Defensive patterns

Strategy: validation

Validate before calling

import zipfile

def is_valid_zip(file_bytes: bytes) -> bool:
    if len(file_bytes) < 4 or file_bytes[:4] != b'PK\x03\x04':
        return False
    try:
        with zipfile.ZipFile(io.BytesIO(file_bytes), 'r') as z:
            return z.testzip() is None
    except zipfile.BadZipFile:
        return False

# before calling import_profile_from_bytes:
if not is_valid_zip(file_bytes):
    raise HTTPException(400, 'Please upload a valid .zip produced by the exporter.')

Type guard

null

Try / catch

try:
    profile = await import_profile_from_bytes(file_bytes, db)
except ValueError as e:
    if str(e) == 'Invalid ZIP file':
        raise HTTPException(400, 'The uploaded file is not a valid ZIP archive.')
    raise HTTPException(400, str(e))

Prevention

When it happens

Trigger: Call to the profile import function with file_bytes that fails zipfile.ZipFile(BytesIO(file_bytes), 'r'). Happens when a user uploads a .tar/.gz, a raw WAV, an HTML error page from a failed download, or a file that was cut off mid-transfer.

Common situations: Browser sent the wrong file due to a renamed extension; a proxy/CDN returned an HTML error page with a .zip content-type; the file was chunked and the last chunk was dropped; the user is uploading a generations ZIP into the profile-import endpoint.

Related errors


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