dotnet/aspnetcore · error · Error
Blazor has already started.
Error message
Blazor has already started.
What it means
Thrown by boot() in Boot.WebView.ts when the module-level 'started' flag is already true. This guards the Blazor WebView boot path (used inside native shells via IPC), which must attach the DotNet dispatcher, start the IPC receiver, and run initializers exactly once per WebView instance.
Solutions
- Initialize Blazor in the WebView exactly once; reload the WebView (full navigation) if you truly need a fresh Blazor instance.
- Guard any native-initiated boot calls with a flag set on first completion.
- Avoid calling Blazor.start() from multiple native lifecycle events.
- Inspect the WebView bootstrap script inclusion for duplication.
Example fix
// before — native handler re-boots on resume
void OnResume() { webView.CoreWebView2.ExecuteScriptAsync("Blazor.start()"); }
// after — only boot once per WebView navigation
bool booted;
void OnResume() {
if (!booted) { webView.CoreWebView2.ExecuteScriptAsync("Blazor.start()"); booted = true; }
} Defensive patterns
Strategy: validation
Validate before calling
// Boot Blazor in the WebView exactly once per navigation.
let webViewBooted;
function bootWebViewOnce() {
if (webViewBooted) return;
webViewBooted = true;
Blazor.start();
} Try / catch
try {
await Blazor.start();
} catch (e) {
if (/already started/.test(e.message)) return;
throw e;
} Prevention
- Reload the WebView (full navigation) when you need a fresh Blazor instance.
- Drive boot from a single native lifecycle event, not several.
- Guard native re-init handlers with a 'booted' flag.
- Confirm the bootstrap script is included once in the WebView HTML.
When it happens
Trigger: Calling the WebView boot path twice in the same WebView, or framework re-entry (e.g. native host triggering re-initialization while a boot is in flight or completed).
Common situations: Native shell code that re-initializes the WebView without reloading it; misconfigured WebView lifecycle hooks re-running boot on resume; HMR-style reload inside the WebView; bundler duplication evaluating Boot.WebView.ts twice.
Related errors
- Blazor has already started.
- Blazor has already started.
- Blazor has already started.
- Blazor Server has already started.
- Blazor WebAssembly has already started.
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/e345705e58a80dc5.
Report an issue: GitHub.
Appendix: source
Thrown at src/Components/Web.JS/src/Boot.WebView.ts:20
// The .NET Foundation licenses this file to you under the MIT license.
import { DotNet } from '@microsoft/dotnet-js-interop';
import { Blazor } from './GlobalExports';
import { shouldAutoStart } from './BootCommon';
import { internalFunctions as navigationManagerFunctions } from './Services/NavigationManager';
import { startIpcReceiver } from './Platform/WebView/WebViewIpcReceiver';
import { sendAttachPage, sendBeginInvokeDotNetFromJS, sendEndInvokeJSFromDotNet, sendByteArray, sendLocationChanged, sendLocationChanging } from './Platform/WebView/WebViewIpcSender';
import { fetchAndInvokeInitializers } from './JSInitializers/JSInitializers.WebView';
import { receiveDotNetDataStream } from './StreamingInterop';
import { WebRendererId } from './Rendering/WebRendererId';
let started = false;
export let dispatcher: DotNet.ICallDispatcher;
async function boot(): Promise<void> {
if (started) {
throw new Error('Blazor has already started.');
}
started = true;
dispatcher = DotNet.attachDispatcher({
beginInvokeDotNetFromJS: sendBeginInvokeDotNetFromJS,
endInvokeJSFromDotNet: sendEndInvokeJSFromDotNet,
sendByteArray: sendByteArray,
});
const jsInitializer = await fetchAndInvokeInitializers();
startIpcReceiver();
Blazor._internal.receiveWebViewDotNetDataStream = receiveWebViewDotNetDataStream;
navigationManagerFunctions.enableNavigationInterception(WebRendererId.WebView);
navigationManagerFunctions.listenForNavigationEvents(WebRendererId.WebView, sendLocationChanged, sendLocationChanging);
View on GitHub (pinned to 3600ca084e)