{"record":{"id":"31da952cbd6c504b","repo":"BabylonJS/Babylon.js","slug":"projectfile-invalid-project-bundle-missing-proj","errorCode":null,"errorMessage":"ProjectFile: Invalid project bundle — missing project.json","messagePattern":"ProjectFile: Invalid project bundle — missing project\\.json","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/sharedUiComponents/src/projects/projectFile.ts","lineNumber":403,"sourceCode":"\r\n/**\r\n * Loads a `.babylonproj` zip bundle into a scene. Extracts all files, creates\r\n * blob URLs for bundled assets, and loads the project through SAM.\r\n *\r\n * @param scene - The scene to load the project into.\r\n * @param zipFile - The `.babylonproj` zip file to load.\r\n */\r\nexport async function LoadProjectFileAsync(scene: Scene, zipFile: File): Promise<void> {\r\n    const arrayBuffer = await zipFile.arrayBuffer();\r\n    const { unzip, strFromU8 } = await import(\"fflate\");\r\n    const extracted = await new Promise<Record<string, Uint8Array>>((resolve, reject) => {\r\n        unzip(new Uint8Array(arrayBuffer), (err, data) => (err ? reject(err instanceof Error ? err : new Error(String(err))) : resolve(data)));\r\n    });\r\n\r\n    // Parse project.json\r\n    const projectJsonBytes = extracted[\"project.json\"];\r\n    if (!projectJsonBytes) {\r\n        throw new Error(\"ProjectFile: Invalid project bundle — missing project.json\");\r\n    }\r\n    const projectJson = JSON.parse(strFromU8(projectJsonBytes));\r\n\r\n    // Create blob URLs for all bundled files and rewrite asset URLs\r\n    for (const [, entry] of Object.entries(projectJson.assets as Record<string, { url: string }>)) {\r\n        const filename = entry.url;\r\n        const fileBytes = extracted[filename];\r\n        if (fileBytes) {\r\n            const mimeType = GuessMimeType(filename);\r\n            // Use a named File so LoadAssetContainerAsync can detect the\r\n            // format from the filename (blob URLs alone have no extension).\r\n            const file = new File([fileBytes as BlobPart], filename, { type: mimeType });\r\n            const blobUrl = URL.createObjectURL(file);\r\n            entry.url = blobUrl;\r\n        }\r\n        // If no file found in zip, assume the URL is a remote reference — leave it as-is\r\n    }\r\n\r","sourceCodeStart":385,"sourceCodeEnd":421,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/sharedUiComponents/src/projects/projectFile.ts#L385-L421","documentation":"LoadProjectFileAsync unzips a project bundle and expects an entry literally named 'project.json' at the archive root. If the zip contains no such entry, the bundle is considered invalid and the promise rejects with this error.","triggerScenarios":"Passing an ArrayBuffer of a zip that lacks a root-level project.json — e.g. the project.json is nested in a subfolder, the zip contains only assets, or the wrong file was uploaded.","commonSituations":"Re-zipping an extracted project with an extra wrapping folder (macOS Archive Utility / right-click compress), uploading the assets zip instead of the project bundle, or exporting from a tool that names the manifest differently.","solutions":["Re-create the zip so project.json sits at the archive root (select the folder's CONTENTS, not the folder itself).","Verify the bundle contents by listing the zip entries before loading.","Ensure you downloaded the project bundle (not a partial assets archive).","Re-export the project from the editor to produce a valid bundle."],"exampleFix":"// before (zip layout)\nmyProject/project.json\nmyProject/assets/...\n// after (zip layout)\nproject.json\nassets/...","handlingStrategy":"validation","validationCode":"const zip = await JSZip.loadAsync(arrayBuffer);\nif (!zip.file('project.json')) {\n    throw new Error('bundle is missing root project.json');\n}\nawait LoadProjectFileAsync(arrayBuffer, ...);","typeGuard":"function bundleHasManifest(entries: Record<string, Uint8Array>): entries is Record<string, Uint8Array> & { 'project.json': Uint8Array } {\n    return Boolean(entries['project.json']);\n}","tryCatchPattern":"try {\n    await LoadProjectFileAsync(buffer, name);\n} catch (e) {\n    if (e instanceof Error && e.message.includes('missing project.json')) {\n        // prompt user to re-upload a valid project bundle\n    } else throw e;\n}","preventionTips":["Zip the folder CONTENTS so project.json is at the archive root, not nested.","List zip entries and assert project.json exists before loading.","Standardize on a bundler script instead of OS right-click compression."],"tags":["zip","deserialization","file-loading"],"backgroundTag":"missing-archive-entry","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}