{"record":{"id":"139b62d6dd8d0176","repo":"BabylonJS/Babylon.js","slug":"smartassetserializer-invalid-asset-map-expected","errorCode":null,"errorMessage":"SmartAssetSerializer: Invalid asset map — expected an object.","messagePattern":"SmartAssetSerializer: Invalid asset map — expected an object\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/core/src/SmartAssets/smartAssetSerializer.ts","lineNumber":43,"sourceCode":" * each loaded `AssetContainer` after load. Persisting it would risk staleness.\n */\nexport interface ISerializedSmartAssetMap {\n    /** Schema version. Must be 1 for the current version. */\n    readonly version: 1;\n\n    /** Map of asset keys to their serialized entries. */\n    readonly assets: Record<string, ISerializedSmartAssetEntry>;\n}\n\n/**\n * Validates and parses a serialized smart asset map document.\n * @param data - The raw data to validate (typically parsed JSON).\n * @returns The validated document.\n * @throws If the data does not conform to the expected schema.\n */\nexport function DeserializeSmartAssetMap(data: unknown): ISerializedSmartAssetMap {\n    if (!data || typeof data !== \"object\") {\n        throw new Error(\"SmartAssetSerializer: Invalid asset map — expected an object.\");\n    }\n\n    const doc = data as Record<string, unknown>;\n\n    if (doc.version !== 1) {\n        throw new Error(`SmartAssetSerializer: Unsupported asset map version \"${doc.version}\". Expected version 1.`);\n    }\n\n    if (!doc.assets || typeof doc.assets !== \"object\" || Array.isArray(doc.assets)) {\n        throw new Error(\"SmartAssetSerializer: Invalid asset map — 'assets' must be an object.\");\n    }\n\n    const assets = doc.assets as Record<string, unknown>;\n    for (const [key, entry] of Object.entries(assets)) {\n        if (!entry || typeof entry !== \"object\") {\n            throw new Error(`SmartAssetSerializer: Invalid entry for key \"${key}\" — expected an object.`);\n        }\n        const entryObj = entry as Record<string, unknown>;","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/core/src/SmartAssets/smartAssetSerializer.ts#L25-L61","documentation":"DeserializeSmartAssetMap validates raw (typically JSON.parse'd) data against the smart asset map schema. The first check requires the input to be a non-null object; null, undefined, arrays-as-top-level (arrays still pass typeof 'object' — this check mainly catches null/primitives), strings, numbers, and booleans all throw this error. It guards against feeding the deserializer empty, truncated, or non-JSON payloads.","triggerScenarios":"Passing JSON.parse(null/undefined/'') results into DeserializeSmartAssetMap; DeserializeProject receiving an empty file, an empty fetch response, or a 404/error body; passing a string of JSON instead of the parsed object; passing null explicitly as the data argument.","commonSituations":"A project file saved as empty due to a crashed write; a network layer returning an error string that got passed through unparsed; reading localStorage before the project was ever saved (null); calling the API with raw text fetched via response.text() instead of response.json().","solutions":["Parse the payload before deserializing: DeserializeSmartAssetMap(JSON.parse(rawText)).","Check the fetch/read result for emptiness or HTTP errors before calling the deserializer.","Handle first-run cases where no saved project exists (null from storage) by constructing a default { version: 1, assets: {} } map.","Log/inspect the raw payload to confirm it is the expected project JSON and not an error page or message."],"exampleFix":"// before\nconst doc = DeserializeSmartAssetMap(await response.text());\n// after\nconst raw = await response.text();\nif (!raw) {\n    throw new Error(\"Project file is empty\");\n}\nconst doc = DeserializeSmartAssetMap(JSON.parse(raw));","handlingStrategy":"validation","validationCode":"function isValidAssetMapPayload(data: unknown): data is Record<string, unknown> {\n    return data !== null && typeof data === \"object\" && !Array.isArray(data);\n}\n// call site:\nconst parsed = JSON.parse(raw);\nif (!isValidAssetMapPayload(parsed)) {\n    throw new Error(\"Project file is corrupt or empty\");\n}\nconst doc = DeserializeSmartAssetMap(parsed);","typeGuard":"function isSmartAssetMapCandidate(data: unknown): data is Record<string, unknown> {\n    return typeof data === \"object\" && data !== null;\n}","tryCatchPattern":"let doc: ISerializedSmartAssetMap;\ntry {\n    doc = DeserializeSmartAssetMap(parsed);\n} catch (e) {\n    if (e instanceof Error && e.message.includes('expected an object')) {\n        doc = { version: 1, assets: {} }; // fall back to an empty project\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always JSON.parse before calling DeserializeSmartAssetMap — never pass raw text.","Check file size/emptiness and HTTP status before parsing fetched project files.","Treat null from localStorage/first-run as 'no project' and build a default map.","Add a smoke test round-tripping Serialize -> Deserialize to catch payload regressions.","Guard against top-level arrays if your producer could emit them."],"tags":["serialization","schema-validation","smart-assets","deserialization"],"backgroundTag":"invalid-payload-schema","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}