dotnet/aspnetcore · error · Error
Blazor WebAssembly has already started.
Error message
Blazor WebAssembly has already started.
What it means
Thrown by startWebAssembly in Boot.WebAssembly.Common when the module-level startPromise is already defined. startWebAssembly runs the WebAssembly startup core (loading the Mono platform, root components, and entry point) and is intended to run exactly once; a second call would re-load the runtime and re-fire initializers.
Solutions
- Call Blazor.start() once; disable autostart if you start manually.
- Guard the call site with a memoized promise.
- Investigate bundler/HMR configuration if the boot module is being evaluated multiple times.
- Ensure the host page includes the boot script exactly once.
Example fix
// before
window.addEventListener('online', () => Blazor.start()); // fires after autostart
// after — single guarded entry
let startP;
function startOnce(){ return startP ?? (startP = Blazor.start()); } Defensive patterns
Strategy: validation
Validate before calling
// Guard startWebAssembly entry so startPromise is created once.
let startPromise;
function ensureWasmStarted() {
return startPromise ?? (startPromise = Blazor.start());
} Try / catch
try {
await Blazor.start();
} catch (e) {
if (/already started/.test(e.message)) return;
throw e;
} Prevention
- Boot WebAssembly once per page.
- Disable autostart when starting manually.
- Investigate duplicate script inclusions.
- Recover from partial-start errors by reloading the page rather than re-booting.
When it happens
Trigger: Calling the WebAssembly boot path twice per page, either via two Blazor.start() invocations or via framework re-entry.
Common situations: Autostart plus manual start; an error-recovery routine that calls start again after a partial success; HMR re-evaluating Boot.WebAssembly.Common; duplicate script tags.
Related errors
- Blazor has already started.
- WebAssembly options have already been configured.
- Blazor has already started.
- Blazor has already started.
- Blazor has already started.
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/387ebf91bd374f2d.
Report an issue: GitHub.
Appendix: 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 loseView on GitHub (pinned to 3600ca084e)