{"record":{"id":"dbec934f62aadef2","repo":"BabylonJS/Babylon.js","slug":"smartassetserializer-failed-to-fetch-source","errorCode":null,"errorMessage":"SmartAssetSerializer: Failed to fetch \"${source}\" — HTTP ${response.status}","messagePattern":"SmartAssetSerializer: Failed to fetch \"(.+?)\" — HTTP (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/core/src/SmartAssets/smartAssetSerializer.ts","lineNumber":117,"sourceCode":" */\nexport function MakeRelative(url: string, baseUrl: string): string {\n    const folder = Tools.GetFolderPath(baseUrl);\n    if (url.startsWith(folder)) {\n        return url.substring(folder.length);\n    }\n    return url;\n}\n\n/**\n * Reads a JSON source from a string URL, File object, or pre-parsed object.\n * @param source - The source to read.\n * @returns A promise resolving to the parsed JSON data.\n */\nexport async function ReadJsonSourceAsync(source: string | File | object): Promise<unknown> {\n    if (typeof source === \"string\") {\n        const response = await fetch(source);\n        if (!response.ok) {\n            throw new Error(`SmartAssetSerializer: Failed to fetch \"${source}\" — HTTP ${response.status}`);\n        }\n        return await response.json();\n    }\n\n    if (source instanceof File) {\n        return await new Promise<unknown>((resolve, reject) => {\n            const reader = new FileReader();\n            reader.onload = () => {\n                try {\n                    resolve(JSON.parse(reader.result as string));\n                } catch {\n                    reject(new Error(`SmartAssetSerializer: Failed to parse JSON from file \"${source.name}\".`));\n                }\n            };\n            reader.onerror = () => reject(new Error(`SmartAssetSerializer: Failed to read file \"${source.name}\".`));\n            reader.readAsText(source);\n        });\n    }","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/core/src/SmartAssets/smartAssetSerializer.ts#L99-L135","documentation":"ReadJsonSourceAsync fetches JSON from a URL when given a string source. If the fetch completes but response.ok is false (any non-2xx HTTP status), the function throws this error embedding the requested source URL and the HTTP status code. It signals the remote JSON could not be retrieved, as opposed to a network failure or malformed JSON body.","triggerScenarios":"Calling ReadJsonSourceAsync(\"https://host/file.json\") (or the SmartAsset serializer import/registry paths that call it via 'raw') where the server responds 404 (file not found), 403 (forbidden/private asset), 401, 500, or any other non-OK status.","commonSituations":"Typo in the asset URL or wrong path/filename on the CDN; asset uploaded to a private bucket without signed URL; CORS/proxy returning 403; file deleted or moved after the asset map was authored; server outage returning 5xx; staging URL referenced from production or vice versa.","solutions":["Open the URL from the error message in a browser or curl -I to confirm the HTTP status and fix the path/filename (404 usually means wrong URL).","If 401/403, add authentication (signed URL, token header) or make the asset publicly readable.","Verify the deployment: re-upload the missing JSON asset to the CDN and update the asset map url entries.","Add retry with backoff for transient 5xx responses, and check server logs if the status is 500."],"exampleFix":"// before\nconst data = await ReadJsonSourceAsync(\"https://cdn.example.com/assets/assest-map.json\"); // 404 typo\n\n// after\nconst url = \"https://cdn.example.com/assets/asset-map.json\";\nconst res = await fetch(url, { method: \"HEAD\" });\nif (!res.ok) throw new Error(`Asset JSON unavailable: ${res.status} ${url}`);\nconst data = await ReadJsonSourceAsync(url);","handlingStrategy":"retry","validationCode":"async function assertReachable(url) {\n  const res = await fetch(url, { method: \"HEAD\" });\n  if (!res.ok) throw new Error(`Pre-check failed for ${url}: HTTP ${res.status}`);\n}","typeGuard":null,"tryCatchPattern":"async function loadJsonWithRetry(url, retries = 3) {\n  for (let i = 0; ; i++) {\n    try {\n      return await ReadJsonSourceAsync(url);\n    } catch (e) {\n      const m = e.message.match(/HTTP (\\d+)/);\n      const status = m && Number(m[1]);\n      if (status && status >= 500 && i < retries) { await new Promise(r => setTimeout(r, 2 ** i * 250)); continue; }\n      if (status === 404 || status === 403) console.error(`Asset JSON ${status} at ${url}: fix URL or auth`);\n      throw e;\n    }\n  }\n}","preventionTips":["HEAD-check asset URLs in CI so broken CDN links are caught before release.","Prefer relative, versioned paths generated by the build over hand-typed URLs.","Ensure private buckets serve via signed URLs or correct public-read ACL.","Monitor CDN/server logs for 4xx/5xx on asset JSON routes."],"tags":["network","http","fetch","smart-assets"],"backgroundTag":"http-request-failed","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}