dotnet/runtime · error · Error
Failed to load resource
Error message
Failed to load resource '${asset.name}' from '${asset.resolvedUrl}'. HTTP status: ${response.status} ${response.statusText} What it means
Thrown by fetchBytes() when the HTTP response was obtained but response.ok is false AND the asset is not marked optional. This is a real server reply with an error status (4xx/5xx). The status code and statusText tell you exactly what the server returned. Contrast with 101 which fires when no response is obtained at all.
Solutions
- Run dotnet publish again and confirm the file exists under bin/Release/net*/publish/wwwroot/_framework/.
- Match the exact filename (case, extension) the boot manifest references against what is deployed.
- Add anonymous static-file auth for /_framework or pass credentials via asset.useCredentials=true.
- Fix server MIME types (application/wasm for .wasm) and rewrite rules so the file is served.
- If the asset is genuinely optional (e.g. satellite PDB), mark isOptional=true or set loaderConfig.ignorePdbLoadErrors=true.
Example fix
// before: 404 because satellite .dll not published // after: ensure publish includes all satellites dotnet publish -c Release --self-contained // or mark optional loaderConfig.ignorePdbLoadErrors = true;
Defensive patterns
Strategy: validation
Validate before calling
async function assetExists(url: string): Promise<boolean> {
const r = await fetch(url, { method: 'HEAD' });
return r.ok;
}
// before create():
for (const a of loaderConfig.resources!.assembly) {
if (!await assetExists(a.resolvedUrl!)) console.warn('Missing', a.name);
} Type guard
function hasOkStatus(r: Response): boolean { return r.ok; } Try / catch
try { await dotnet.create(); } catch (e) {
const m = (e as Error).message.match(/HTTP status: (\d{3})/);
if (m) handleHttpError(Number(m[1]));
else throw e;
} Prevention
- Run dotnet publish and verify every file in _framework is uploaded to the server.
- Add a CI step that curls each asset URL from the boot manifest and asserts 200.
- Mark genuinely optional assets isOptional=true so a 4xx does not abort startup.
- Configure server MIME types for .wasm (application/wasm) and .dll.
When it happens
Trigger: HTTP 404 because the asset was not published to wwwroot/_framework. HTTP 401/403 from authenticated endpoints. HTTP 500 from a misconfigured server. asset.isOptional is unset (default) so any non-2xx aborts startup. Server returns 200 with wrong content but wrong status set by a custom callback.
Common situations: Publishing the app but forgetting 'dotnet publish' so _framework is stale/missing assemblies. Renaming a DLL and not republishing. CDN cache serving a stale 404. IIS missing MIME map for .wasm/.dll (would actually 404 the file). Authentication cookie expired so the static file endpoint returns 401. web.config rewrite rules swallowing the file path.
Related errors
- Failed to load resource
- Failed to load WebAssembly module. HTTP status
- Failed to load Webcil module
- Azure failed to download
- BrowserHttpWriteStream.Rejected
AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10).
Data as JSON: /api/errors/2f586be136b15e4c.
Report an issue: GitHub.
Appendix: source
Thrown at src/native/libs/Common/JavaScript/loader/assets.ts:363
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) {
// 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}`);View on GitHub (pinned to 60108ba66e)