BabylonJS/Babylon.js · error

ProjectFile: Invalid project file — expected an object.

Error message

ProjectFile: Invalid project file — expected an object.

What it means

DeserializeProject is the entry validator for project documents: it requires non-null object input and (next) version === 2. Non-object data means the raw payload isn't a project document at all — usually a parse failure, an error response, or a wrong file fed to the loader.

Source

Thrown at packages/dev/sharedUiComponents/src/projects/projectFile.ts:286

        }
    } finally {
        // Always restore the render loops, even if loading threw — otherwise
        // the canvas stays frozen forever and the user has no way to recover.
        for (const loop of savedRenderLoops) {
            engine.runRenderLoop(loop);
        }
    }
}

/**
 * Validates and parses a serialized project document.
 * @param data - The raw data to validate (typically parsed JSON).
 * @returns The validated project document.
 * @throws If the data does not conform to the expected schema.
 */
export function DeserializeProject(data: unknown): ISerializedProject {
    if (!data || typeof data !== "object") {
        throw new Error("ProjectFile: Invalid project file — expected an object.");
    }

    const doc = data as Record<string, unknown>;

    if (doc.version !== 2) {
        throw new Error(`ProjectFile: Unsupported project version "${doc.version}". Expected version 2.`);
    }

    // Validate the asset map portion
    DeserializeSmartAssetMap({ version: 1, assets: doc.assets });

    // Validate overrides array
    if (!Array.isArray(doc.overrides)) {
        throw new Error("ProjectFile: Invalid project file — 'overrides' must be an array.");
    }

    // Validate optional companion bindings (shape-only check)
    if (doc.companionBindings !== undefined) {

View on GitHub (pinned to 0592b347b8)

Solutions

  1. JSON.parse the raw content before calling DeserializeProject.
  2. Verify the fetched/loaded file is actually a project file (check content-type/first bytes).
  3. Confirm doc.version === 2 and fix or migrate files saved with other versions.
  4. Handle fetch/load failures so error responses are not passed to the deserializer.

Example fix

// before
DeserializeProject(await res.text()); // string, not object
// after
const data = JSON.parse(await res.text());
DeserializeProject(data); // validated object, version 2
Defensive patterns

Strategy: validation

Validate before calling

function canDeserializeProject(data) {
  return !!data && typeof data === "object" && !Array.isArray(data) && data.version === 2;
}
const parsed = JSON.parse(raw);
if (canDeserializeProject(parsed)) DeserializeProject(parsed);

Type guard

function isProjectDocument(v: unknown): v is ISerializedProject {
  return !!v && typeof v === "object" && !Array.isArray(v) && (v as any).version === 2;
}

Try / catch

// try {
//   return DeserializeProject(parsed);
// } catch (e) {
//   if (String(e.message).includes("Invalid project file")) {
//     throw new Error("Loaded content is not a project document — check the file/endpoint", { cause: e });
//   }
//   throw e;
// }

Prevention

When it happens

Trigger: Calling DeserializeProject(data) with null/undefined, a JSON string (not parsed), an array, or an error object; LoadProjectAsync receiving non-JSON content from the file/input stream.

Common situations: Fetching a project from a URL that returned HTML or an error JSON envelope; double-encoding (passing a string instead of parsed JSON); opening a file of the wrong type saved with a project extension.

Related errors


AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30). Data as JSON: /api/errors/93ae9b1797b8e555. Report an issue: GitHub.