dotnet/aspnetcore · error · Error

Blazor WebAssembly has already started.

Error message

Blazor WebAssembly has already started.

What it means

Thrown by startWebAssembly when the module-level startPromise is already defined. startWebAssembly loads the Mono runtime and entry point exactly once; a second call indicates the WebAssembly platform was asked to boot twice.

Source

Thrown at src/Components/Web.JS/src/Boot.WebAssembly.Common.ts:75

}

export function setWebAssemblyOptions(initializersReady: Promise<Partial<WebAssemblyStartOptions>>) {
  if (options) {
    throw new Error('WebAssembly options have already been configured.');
  }
  setOptions(initializersReady);


  async function setOptions(initializers: Promise<Partial<WebAssemblyStartOptions>>) {
    const configuredOptions = await initializers;
    options = configuredOptions;
    resolveInitializersPromise();
  }
}

export function startWebAssembly(components: RootComponentManager<WebAssemblyComponentDescriptor>, options: WebAssemblyServerOptions | undefined): Promise<void> {
  if (startPromise !== undefined) {
    throw new Error('Blazor WebAssembly has already started.');
  }

  startPromise = new Promise(startCore.bind(null, components, options));

  return startPromise;
}

async function startCore(components: RootComponentManager<WebAssemblyComponentDescriptor>, options: WebAssemblyServerOptions | undefined, resolve, _) {
  if (inAuthRedirectIframe()) {
    // eslint-disable-next-line @typescript-eslint/no-empty-function
    await new Promise(() => { }); // See inAuthRedirectIframe for explanation
  }

  const platformLoadPromise = loadWebAssemblyPlatformIfNotStarted(options);

  addDispatchEventMiddleware((browserRendererId, eventHandlerId, continuation) => {
    // It's extremely unusual, but an event can be raised while we're in the middle of synchronously applying a
    // renderbatch. For example, a renderbatch might mutate the DOM in such a way as to cause an <input> to lose

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Guard with hasStartedWebAssembly() (Boot.WebAssembly.Common.ts:200) before starting.
  2. Pick autostart OR a single manual Blazor.start(); not both.
  3. Ensure the WebAssembly bundle is loaded once per page/iframe.

Example fix

// before
Blazor.start();
// ... later ...
Blazor.start(); // throws

// after
import { hasStartedWebAssembly } from './Boot.WebAssembly.Common';
if (!hasStartedWebAssembly()) {
  await Blazor.start();
}
Defensive patterns

Strategy: validation

Validate before calling

import { hasStartedWebAssembly } from './Boot.WebAssembly.Common';

function startWasmOnce(components: any, opts: any) {
  if (hasStartedWebAssembly()) return Promise.resolve();
  return startWebAssembly(components, opts);
}

Type guard

import { hasStartedWebAssembly } from './Boot.WebAssembly.Common';

function wasmStarted(): boolean {
  return hasStartedWebAssembly();
}

Try / catch

try {
  await Blazor.start();
} catch (e) {
  if (/already started/.test((e as Error).message)) return;
  throw e;
}

Prevention

When it happens

Trigger: Calling Blazor.start() twice for a WebAssembly app, or two boot paths both invoking startWebAssembly (e.g. Blazor Web App with an InteractiveWebAssembly render mode plus a manual start).

Common situations: Autostart plus manual start; HMR reloading blazor.webassembly.js; tests or hosting shells that re-invoke boot; an iframe re-running the bundle.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/387ebf91bd374f2d. Report an issue: GitHub.