{"record":{"id":"3bf85a1d6ee5f9a0","repo":"jamiepine/voicebox","slug":"invalid-manifest-json-missing-version","errorCode":null,"errorMessage":"Invalid manifest.json: missing version","messagePattern":"Invalid manifest\\.json: missing version","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/services/export_import.py","lineNumber":154,"sourceCode":"    \"\"\"\n    zip_buffer = io.BytesIO(file_bytes)\n    \n    try:\n        with zipfile.ZipFile(zip_buffer, 'r') as zip_file:\n            # Validate ZIP structure\n            namelist = zip_file.namelist()\n            \n            if \"manifest.json\" not in namelist:\n                raise ValueError(\"ZIP archive missing manifest.json\")\n            \n            if \"samples.json\" not in namelist:\n                raise ValueError(\"ZIP archive missing samples.json\")\n            \n            # Read manifest\n            manifest_data = json.loads(zip_file.read(\"manifest.json\"))\n            \n            if \"version\" not in manifest_data:\n                raise ValueError(\"Invalid manifest.json: missing version\")\n            \n            if \"profile\" not in manifest_data:\n                raise ValueError(\"Invalid manifest.json: missing profile\")\n            \n            profile_data = manifest_data[\"profile\"]\n            \n            # Read samples mapping\n            samples_data = json.loads(zip_file.read(\"samples.json\"))\n            \n            if not isinstance(samples_data, dict):\n                raise ValueError(\"Invalid samples.json: must be a dictionary\")\n            \n            # Get unique profile name\n            original_name = profile_data.get(\"name\", \"Imported Profile\")\n            unique_name = _get_unique_profile_name(original_name, db)\n            \n            # Create profile\n            profile_create = VoiceProfileCreate(","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/services/export_import.py#L136-L172","documentation":"After JSON-parsing manifest.json, the importer requires a 'version' key — it's used to pick the right schema/migration path. Absence is treated as corrupt or unsupported. Raised before the 'profile' key is read.","triggerScenarios":"Hand-authored manifest without version; a manifest from an unknown producer; a manifest stripped during editing.","commonSituations":"Cross-version import where an older/newer format omitted version; manual archive construction; partially-rewritten manifest.","solutions":["Re-export with the current exporter (it always writes version: \"1.0\").","If constructing manually, set manifest.version = \"1.0\".","Don't strip version when editing manifests."],"exampleFix":"// before\n{\"profile\": {\"name\": \"x\"}, \"has_avatar\": false}\n\n// after\n{\"version\": \"1.0\", \"profile\": {\"name\": \"x\", \"description\": \"...\", \"language\": \"en\"}, \"has_avatar\": false}","handlingStrategy":"validation","validationCode":"import io, json, zipfile\n\ndef manifest_has_version(file_bytes) -> bool:\n    with zipfile.ZipFile(io.BytesIO(file_bytes)) as z:\n        return \"version\" in json.loads(z.read(\"manifest.json\"))","typeGuard":"def is_v1_manifest(m) -> bool:\n    return isinstance(m, dict) and m.get(\"version\") == \"1.0\" and \"profile\" in m","tryCatchPattern":"try:\n    await import_profile_from_zip(file_bytes, db)\nexcept ValueError as e:\n    if \"missing version\" in str(e):\n        # not a supported archive\n        ...\n    raise","preventionTips":["Only import archives produced by a known exporter version.","Pre-validate manifest keys (version, profile) client-side."],"tags":["profiles","import","manifest","validation","schema"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}