dotnet/aspnetcore · error · Error
Blazor has already started.
Error message
Blazor has already started.
What it means
Thrown by boot() in Boot.Web.ts (the blazor.web.js entry, used by Blazor Web App SSR + interactive) when its module-level 'started' flag is already true. blazor.web.js is single-shot: it sets up streaming render, enhanced navigation, form validation, and the root component manager in one pass.
Source
Thrown at src/Components/Web.JS/src/Boot.Web.ts:39
import { hasProgrammaticEnhancedNavigationHandler, performProgrammaticEnhancedNavigation } from './Services/NavigationUtils';
import { attachComponentDescriptorHandler, registerAllComponentDescriptors } from './Rendering/DomMerging/DomSync';
import { discoverBrowserConfiguration } from './Services/ComponentDescriptorDiscovery';
import { JSEventRegistry } from './Services/JSEventRegistry';
import { fetchAndInvokeInitializers } from './JSInitializers/JSInitializers.Web';
import { ConsoleLogger } from './Platform/Logging/Loggers';
import { LogLevel } from './Platform/Logging/Logger';
import { resolveOptions, CircuitStartOptions, ReconnectionOptions } from './Platform/Circuits/CircuitStartOptions';
import { JSInitializer } from './JSInitializers/JSInitializers';
import { enableFocusOnNavigate } from './Rendering/FocusOnNavigate';
import { WebAssemblyStartOptions } from './Platform/WebAssemblyStartOptions';
import { createBlazorValidation, ensureNovalidateOnForms } from './Validation';
let started = false;
let rootComponentManager: WebRootComponentManager;
function boot(options?: Partial<WebStartOptions>) : Promise<void> {
if (started) {
throw new Error('Blazor has already started.');
}
started = true;
options = options || {};
options.logLevel ??= LogLevel.Error;
Blazor._internal.isBlazorWeb = true;
// Defined here to avoid inadvertently imported enhanced navigation
// related APIs in WebAssembly or Blazor Server contexts.
Blazor._internal.hotReloadApplied = () => {
if (hasProgrammaticEnhancedNavigationHandler()) {
performProgrammaticEnhancedNavigation(location.href, true);
}
};
const jsEventRegistry = JSEventRegistry.create(Blazor);
rootComponentManager = new WebRootComponentManager(options?.ssr?.circuitInactivityTimeoutMs ?? 2000, jsEventRegistry);
View on GitHub (pinned to 294cab2f9b)
Solutions
- Choose either autostart or a single manual Blazor.start(), not both.
- Guard the call with a window-level started flag before invoking.
- Ensure blazor.web.js is included exactly once per full page load (enhanced navigation does not re-include scripts, but full reloads / iframes might).
Example fix
// before <script src="blazor.web.js" autostart></script> <script>Blazor.start();</script> <!-- throws --> // after <script src="blazor.web.js"></script> <script>Blazor.start();</script>
Defensive patterns
Strategy: validation
Validate before calling
function startWebBlazorOnce(opts?: any) {
if ((window as any).__blazorWebStarted) return Promise.resolve();
(window as any).__blazorWebStarted = true;
return Blazor.start(opts);
} Type guard
function isWebBlazorStarted(): boolean {
return !!(window as any).__blazorWebStarted;
} Try / catch
try {
await Blazor.start(opts);
} catch (e) {
if (/already started/.test((e as Error).message)) return;
throw e;
} Prevention
- Use autostart OR one manual start; not both.
- Include blazor.web.js exactly once per full page load.
- Guard the manual start with a window flag.
- Ensure enhanced navigation does not re-evaluate the boot bundle.
When it happens
Trigger: Calling Blazor.start() twice on a Blazor Web App page; autostart firing together with a manual start; a JS initializer that calls Blazor.start().
Common situations: Including <script src="blazor.web.js" autostart> plus a manual Blazor.start({...}); a navigation/HMR cycle re-evaluating the bundle; a custom layout that re-includes the script tag.
Related errors
- Blazor has already started.
- Blazor WebAssembly has already started.
- Blazor has already started.
- Blazor has already started.
- Circuit options have already been configured.
AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06).
Data as JSON: /api/errors/0aac84c8dffcef1a.
Report an issue: GitHub.