dotnet/runtime · error · Error
Failed to load resource
Error message
Failed to load resource '${asset.name}' from '${asset.resolvedUrl}': ${message} What it means
Wrapped re-throw inside fetchBytes(): loadResource() rejected (network throw, abort, CORS, or an exception thrown by a custom loadBootResourceCallback). The wrapper strips the original .silent flag so the error propagates through exit listeners. Original cause is attached via { cause: err }. Asset bytes could not be obtained at all — no HTTP response was successfully produced.
Solutions
- Open DevTools Network tab and inspect the failing request's status/type; the original cause is in err.cause.
- If CORS, add Access-Control-Allow-Origin for the WASM app origin on the asset server, or host assets same-origin.
- If a custom loadBootResourceCallback throws, guard its body and return a valid Response or string URL.
- If SRI hash mismatch, rebuild so the boot manifest hash matches the published asset bytes.
- If offline/Node, ensure polyfills.ts nodeFs is initialized before fetch, or pass a globalThis.fetch.
Example fix
// before: custom loader throws on auth failure
options.loadBootResource = (type, name, url) => fetch(url); // rejects 401
// after: return a Response, handle auth
options.loadBootResource = (type, name, url, integrity, behavior) =>
fetch(url, { credentials: 'include' }); Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight the URL is reachable and CORS permits the origin
async function canFetchAsset(url: string): Promise<boolean> {
try {
const r = await fetch(url, { method: 'HEAD', mode: 'cors' });
return r.ok || r.status === 405; // 405 = HEAD not allowed but resource exists
} catch { return false; }
} Type guard
function isResponseLike(v: unknown): v is Response {
return !!v && typeof v === 'object' && typeof (v as any).arrayBuffer === 'function' && typeof (v as any).ok === 'boolean';
} Try / catch
try {
await dotnet.create();
} catch (e) {
const cause = (e as Error)?.cause ?? e;
if (/Failed to load resource/.test((e as Error).message)) {
reportToUser('Asset download failed', { url: extractUrl(e), cause });
}
throw e;
} Prevention
- Serve assets same-origin or configure CORS headers explicitly for the app origin.
- Smoke-test asset URLs from the deployment environment in CI before release.
- Wrap any custom loadBootResourceCallback in try/catch and return a controlled Response on error.
- Log err.cause — the wrapped error carries the real network exception.
When it happens
Trigger: Network offline or DNS failure during assembly/PDB/ICU/VFS download. CORS preflight rejection blocking the fetch. A user-supplied loadBootResourceCallback that throws or returns a promise that rejects. TLS/cert error on the asset URL. Service Worker returning a rejecting Response promise.
Common situations: Running the WASM app from a different origin than the assets without CORS headers. Corporate proxy intercepting requests. Misconfigured CDN returning an opaque response. Custom loadBootResourceCallback returning fetch to a private endpoint that 401s and throws. file:// URLs in Node without the fs polyfill resolved. Asset hash mismatch triggers SRI failure (browser throws).
Related errors
- BrowserHttpWriteStream.Rejected
- Azure failed to download
- BrowserHttpWriteStream.Rejected
- Couldn't determine newest 'main' git hash
- Didn't find any collections using
AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10).
Data as JSON: /api/errors/5a98b6186140799a.
Report an issue: GitHub.
Appendix: source
Thrown at src/native/libs/Common/JavaScript/loader/assets.ts:356
}
assetInternal.behavior = "symbols";
assetInternal.isOptional = assetInternal.isOptional || loaderConfig.ignorePdbLoadErrors;
tableText = await fetchText(assetInternal);
} finally {
onDownloadedAsset(assetInternal);
}
dotnetDiagnosticsExports.installNativeSymbols(tableText || "");
}
async function fetchBytes(asset: AssetEntryInternal): Promise<Uint8Array | null> {
dotnetAssert.check(asset && asset.resolvedUrl, "Bad asset.resolvedUrl");
let response: Response;
try {
response = await loadResource(asset);
} catch (err: any) {
// Strip .silent flag from download errors so they are properly reported via exit listeners
const message = err instanceof Error ? err.message : String(err);
throw new Error(`Failed to load resource '${asset.name}' from '${asset.resolvedUrl}': ${message}`, { cause: err });
}
if (!response.ok) {
if (asset.isOptional) {
dotnetLogger.warn(`Optional resource '${asset.name}' failed to load from '${asset.resolvedUrl}'. HTTP status: ${response.status} ${response.statusText}`);
return null;
}
throw new Error(`Failed to load resource '${asset.name}' from '${asset.resolvedUrl}'. HTTP status: ${response.status} ${response.statusText}`);
}
const buffer = await (asset.buffer || response.arrayBuffer());
return new Uint8Array(buffer);
}
async function fetchText(asset: AssetEntryInternal): Promise<string | null> {
dotnetAssert.check(asset && asset.resolvedUrl, "Bad asset.resolvedUrl");
let response: Response;
try {
response = await loadResource(asset);
} catch (err: any) {View on GitHub (pinned to 60108ba66e)