dotnet/aspnetcore · error · Error

Blazor has already started.

Error message

Blazor has already started.

What it means

Thrown by boot() in Boot.Web.ts when the module-level 'started' flag is already true. Boot.Web.ts is the unified web boot (used by Blazor Web App / blazor.web.js) and must initialize the framework exactly once; a second call would re-create the root component manager and re-run initializers.

Solutions

  1. Invoke Blazor.start() exactly once; rely on autostart OR a single manual call, not both.
  2. Memoize the boot promise at the call site so subsequent attempts reuse it.
  3. Disable autostart when you need programmatic control of timing.
  4. Verify your bundler is not duplicating the boot module under HMR.

Example fix

// before
if (needsBoot) Blazor.start({ ... }); // called on every navigation -> throws

// after
let booted;
async function ensureBoot(opts) {
  if (!booted) booted = Blazor.start(opts);
  return booted;
}
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the unified Web boot runs once.
let webBootPromise;
function bootWebOnce(opts) {
  return webBootPromise ?? (webBootPromise = Blazor.start(opts));
}

Try / catch

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

Prevention

When it happens

Trigger: Calling Blazor.start() in a Blazor Web App more than once per page load, including via enhanced-navigation or shell code that re-invokes boot.

Common situations: Autostart combined with a manual Blazor.start(); a multi-page shell re-running boot on client-side navigation; bundler/HMR re-evaluation of Boot.Web.ts; retry logic that calls boot again after a transient failure that actually succeeded.

Related errors


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

Appendix: 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 3600ca084e)