jamiepine/voicebox · error · ValueError
Invalid samples.json: must be a dictionary
Error message
Invalid samples.json: must be a dictionary
What it means
samples.json must JSON-parse to a dict (filename -> reference_text). A list, scalar, or any other top-level JSON type is rejected because the importer iterates samples_data.items() expecting string keys. Raised after JSON parsing succeeds.
Source
Thrown at backend/services/export_import.py:165
if "samples.json" not in namelist:
raise ValueError("ZIP archive missing samples.json")
# Read manifest
manifest_data = json.loads(zip_file.read("manifest.json"))
if "version" not in manifest_data:
raise ValueError("Invalid manifest.json: missing version")
if "profile" not in manifest_data:
raise ValueError("Invalid manifest.json: missing profile")
profile_data = manifest_data["profile"]
# Read samples mapping
samples_data = json.loads(zip_file.read("samples.json"))
if not isinstance(samples_data, dict):
raise ValueError("Invalid samples.json: must be a dictionary")
# Get unique profile name
original_name = profile_data.get("name", "Imported Profile")
unique_name = _get_unique_profile_name(original_name, db)
# Create profile
profile_create = VoiceProfileCreate(
name=unique_name,
description=profile_data.get("description"),
language=profile_data.get("language", "en"),
)
profile = await create_profile(profile_create, db)
# Extract and add samples
profile_dir = config.get_profiles_dir() / profile.id
profile_dir.mkdir(parents=True, exist_ok=True)
View on GitHub (pinned to 51f49dea19)
Solutions
- Re-export with the official exporter.
- If rebuilding, make samples.json a flat object: {"<filename>.wav": "<reference text>"}.
Example fix
// before
[{"file": "a.wav", "text": "hi"}, {"file": "b.wav", "text": "bye"}]
// after
{"a.wav": "hi", "b.wav": "bye"} Defensive patterns
Strategy: validation
Validate before calling
import io, json, zipfile
def samples_is_dict(file_bytes) -> bool:
with zipfile.ZipFile(io.BytesIO(file_bytes)) as z:
return isinstance(json.loads(z.read("samples.json")), dict) Type guard
def is_samples_mapping(v) -> bool:
return isinstance(v, dict) and all(isinstance(k, str) for k in v) Try / catch
try:
await import_profile_from_zip(file_bytes, db)
except ValueError as e:
if "must be a dictionary" in str(e):
# reformat samples.json into a flat {filename: text} mapping
...
raise Prevention
- Validate samples.json top-level type before importing.
- Use the official exporter; don't hand-craft the mapping.
When it happens
Trigger: samples.json contains a JSON array of objects, a bare string/number, or any non-object type — typically produced by a non-conforming migration tool or hand-edit.
Common situations: Third-party migration script emitting an array; manual edit turning the object into a list of records.
Related errors
- Invalid manifest.json: missing version
- Invalid manifest.json: missing profile
- Invalid sample filename: {filename} (must be .wav)
- Sample file not found in ZIP: {zip_path}
- {e}
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/f5b5c024a53e3602.
Report an issue: GitHub.