dotnet/aspnetcore · error
The event ' ' is already registered.
Error message
The event '${eventName}' is already registered. What it means
Thrown by registerCustomEventType when an event name has already been added to the eventTypeRegistry. Blazor disallows duplicate registrations because each custom event name maps to exactly one eventargs-builder; a second mapping would make it ambiguous which data to supply to the handler. The guard runs before any aliasing logic so it catches both genuine duplicates and accidental re-registration.
Solutions
- Search the codebase for duplicate registerCustomEventType calls with the same event name and remove or consolidate them.
- Guard the registration with a check: only register if the event is not already known (call from a module loaded once).
- If two libraries legitimately need the event, pick a unique prefix per library (e.g. 'libA:myevent').
- Ensure the registering script is loaded exactly once via script tags or a singleton import.
Example fix
// before
Blazor.registerCustomEventType('myevent', { createEventArgs: e => ({ value: e.detail }) });
// loaded again elsewhere -> throws
// after: idempotent guard
if (!Blazor._customEventRegistered?.('myevent')) {
Blazor.registerCustomEventType('myevent', { createEventArgs: e => ({ value: e.detail }) });
} Defensive patterns
Strategy: validation
Validate before calling
// Before registering, check if already registered.
// Blazor exposes eventNameAliasRegisteredCallbacks; for a custom guard keep a Set.
const registered = (globalThis.__blazorCustomEvents ??= new Set());
if (!registered.has(eventName)) {
Blazor.registerCustomEventType(eventName, options);
registered.add(eventName);
} Type guard
// No public API to query the registry; track locally.
function isCustomEventRegistered(name: string): boolean {
return (globalThis.__blazorCustomEvents ?? new Set()).has(name);
} Try / catch
try {
Blazor.registerCustomEventType(name, options);
} catch (e) {
if (e instanceof Error && /already registered/.test(e.message)) {
// benign duplicate (e.g. HMR) — ignore
} else { throw e; }
} Prevention
- Centralize custom event registration in a single module loaded once.
- Add a local Set guard so HMR/re-imports don't double-register.
- Prefix event names with your library name to avoid collisions across libraries.
When it happens
Trigger: Calling Blazor.registerCustomEventType('myevent', {...}) twice with the same first argument in the same JS context; loading two JS files that both register the same event; importing a library that registers a custom event after the app already registered it.
Common situations: A shared component library and app code both register 'copy' or 'paste'; a hot-reload / HMR cycle that re-executes the registration module without a guard; bundling two versions of a Blazor extensions package.
Related errors
- The options parameter is required.
- An exception occurred while dispatching a location changed…
- Interop methods are already registered for renderer
- The custom event ' ' cannot have the same name as its…
- The event handler parameter type
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/5a427c98796e7f17.
Report an issue: GitHub.
Appendix: source
Thrown at src/Components/Web.JS/src/Rendering/Events/EventTypes.ts:23
browserEventName?: string;
createEventArgs?: (event: Event) => unknown;
}
const eventTypeRegistry: Map<string, EventTypeOptions> = new Map();
const browserEventNamesToAliases: Map<string, string[]> = new Map();
const createBlankEventArgsOptions: EventTypeOptions = { createEventArgs: () => ({}) };
export const eventNameAliasRegisteredCallbacks: ((aliasEventName: string, browserEventName) => void)[] = [];
export function registerCustomEventType(eventName: string, options: EventTypeOptions): void {
if (!options) {
throw new Error('The options parameter is required.');
}
// There can't be more than one registration for the same event name because then we wouldn't
// know which eventargs data to supply.
if (eventTypeRegistry.has(eventName)) {
throw new Error(`The event '${eventName}' is already registered.`);
}
// When aliasing a browser event, the custom event name must be different from the browser event name
// to avoid double-triggering (once for the browser event, once for the custom event wrapper)
if (options.browserEventName && eventName === options.browserEventName) {
throw new Error(`The custom event '${eventName}' cannot have the same name as its browserEventName '${options.browserEventName}'. Choose a different name for the custom event.`);
}
// If applicable, register this as an alias of the given browserEventName
if (options.browserEventName) {
const aliasGroup = browserEventNamesToAliases.get(options.browserEventName);
if (aliasGroup) {
aliasGroup.push(eventName);
} else {
browserEventNamesToAliases.set(options.browserEventName, [eventName]);
}
// For developer convenience, it's allowed to register the custom event type *after*View on GitHub (pinned to 3600ca084e)