dotnet/aspnetcore · error · Error

For a resource, custom loaders must supply a URI string.

Error message

For a ${resourceType} resource, custom loaders must supply a URI string.

What it means

When a `loadBootResource` callback is supplied, Blazor lets it override the URL for the `dotnetjs` (the runtime JS module) resource. Because the runtime JS must be loaded via a dynamic `import()`, the custom loader can only return a URI string — any other truthy return (e.g. a `Request`/`Response`/`URL`) is unusable for a module import and throws.

Solutions

  1. For `resourceType === 'dotnetjs'`, return a string URL (or `null` to use the default) from `loadBootResource` — never a Request/Response.
  2. Branch your `loadBootResource` by resource type so only fetchable resources use the richer return shapes.
  3. If you don't need to override the runtime JS URL, return `null`/`undefined` for `dotnetjs`.
  4. Coerce with `String(customSrc)` or construct via `new URL(...).toString()` before returning.

Example fix

// before
loadBootResource: (type, name) => new Request(`/cdn/${name}`) // throws for dotnetjs

// after
loadBootResource: (type, name) =>
  type === 'dotnetjs' ? `/cdn/${name}` : new Request(`/cdn/${name}`)
Defensive patterns

Strategy: validation

Validate before calling

loadBootResource: (type, name, defaultUri) => {
  if (type === 'dotnetjs') return typeof customSrc === 'string' ? customSrc : null;
  return customSrc; // Request/Response ok for other types
}

Type guard

function isUriString(v: unknown): v is string { return typeof v === 'string'; }

Try / catch

try { await Blazor.start({ loadBootResource }); }
catch (e) { if (/must supply a URI string/.test(e.message)) { /* fix loader for dotnetjs */ } throw e; }

Prevention

When it happens

Trigger: Supplying `loadBootResource` in `Blazor.start({ loadBootResource })` and, for a `dotnetjs` resource, returning a truthy non-string value (Request, Response, URL object, or similar).

Common situations: Reusing a generic `loadBootResource` implementation (that returns `Request`/`Response` for fetchable assets) for every resource type without special-casing `dotnetjs`; caching/proxying logic returning a URL object instead of a string; copy-pasting a loader that works for `assembly`/`pdb` but not `dotnetjs`.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/4598b3ab9c1c1179. Report an issue: GitHub.

Appendix: source

Thrown at src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts:137

};

async function importDotnetJs(startOptions: Partial<WebAssemblyStartOptions>): Promise<ModuleAPI> {
  const browserSupportsNativeWebAssembly = typeof WebAssembly !== 'undefined' && WebAssembly.validate;
  if (!browserSupportsNativeWebAssembly) {
    throw new Error('This browser does not support WebAssembly.');
  }


  // Allow overriding the URI from which the dotnet.*.js file is loaded
  if (startOptions.loadBootResource) {
    const resourceType: WebAssemblyBootResourceType = 'dotnetjs';
    const customSrc = startOptions.loadBootResource(resourceType, 'dotnet.js', '_framework/dotnet.js', '', 'js-module-dotnet');
    if (typeof (customSrc) === 'string') {
      const absoluteSrc = (new URL(customSrc, document.baseURI)).toString();
      return await import(/* webpackIgnore: true */ absoluteSrc);
    } else if (customSrc) {
      // Since we must load this via a import, it's only valid to supply a URI (and not a Request, say)
      throw new Error(`For a ${resourceType} resource, custom loaders must supply a URI string.`);
    }
  }

  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore: This dynamic import is handled at runtime and does not need a type declaration.
  return await import(/* webpackIgnore: true */ './dotnet.js');
}

function prepareRuntimeConfig(options: Partial<WebAssemblyStartOptions>, onConfigLoadedCallback?: (loadedConfig: MonoConfig) => void): DotnetModuleConfig {
  const config: MonoConfig = {
    maxParallelDownloads: 1000000, // disable throttling parallel downloads
    enableDownloadRetry: false, // disable retry downloads
  };

  if (options.environment) {
    config.applicationEnvironment = options.environment;
  }

View on GitHub (pinned to 3600ca084e)