angular/angular · error · Error
`preventDefault` called during event replay.
Error message
`preventDefault` called during event replay.
What it means
The EventDispatcher from @angular/core/primitives/event-dispatch replays events that were queued by the EventContract after the browser's native dispatch has finished. During replay it patches the event's preventDefault to call through and then throw, because by replay time the browser default action has already run and preventDefault would have no effect. The event is marked by patching eventPhase to EventPhase.REPLAY (101), which you can check before calling preventDefault.
Source
Thrown at packages/core/primitives/event-dispatch/src/event_dispatcher.ts:129
};
patchEventInstance(event, 'stopPropagation', stopPropagation);
patchEventInstance(event, 'stopImmediatePropagation', stopPropagation);
}
function propagationStopped(eventInfoWrapper: EventInfoWrapper) {
const event = eventInfoWrapper.getEvent();
return !!event[PROPAGATION_STOPPED_SYMBOL];
}
function prepareEventForReplay(eventInfoWrapper: EventInfoWrapper) {
const event = eventInfoWrapper.getEvent();
const target = eventInfoWrapper.getTargetElement();
const originalPreventDefault = event.preventDefault.bind(event);
patchEventInstance(event, 'target', target);
patchEventInstance(event, 'eventPhase', EventPhase.REPLAY);
patchEventInstance(event, 'preventDefault', () => {
originalPreventDefault();
throw new Error(
PREVENT_DEFAULT_ERROR_MESSAGE + (ngDevMode ? PREVENT_DEFAULT_ERROR_MESSAGE_DETAILS : ''),
);
});
patchEventInstance(event, 'composedPath', () => {
throw new Error(
COMPOSED_PATH_ERROR_MESSAGE + (ngDevMode ? COMPOSED_PATH_ERROR_MESSAGE_DETAILS : ''),
);
});
}
function prepareEventForDispatch(eventInfoWrapper: EventInfoWrapper) {
const event = eventInfoWrapper.getEvent();
const currentTarget = eventInfoWrapper.getAction()?.element;
if (currentTarget) {
patchEventInstance(event, 'currentTarget', currentTarget, {
// `currentTarget` is going to get reassigned every dispatch.
configurable: true,
});View on GitHub (pinned to 51cb07e980)
Solutions
- Guard the call: only invoke preventDefault() when event.eventPhase !== EventPhase.REPLAY (101)
- For defaults that must truly be suppressed, handle the event during native dispatch (capture-phase native listener) instead of in the replayed contract handler
- Restructure so the default action is acceptable during replay and correct state afterwards (e.g., re-run navigation logic) rather than cancelling the event
Example fix
// before
handler: (event: Event) => {
event.preventDefault(); // throws during replay
}
// after
import {EventPhase} from '@angular/core/primitives/event-dispatch';
handler: (event: Event) => {
if (event.eventPhase !== EventPhase.REPLAY) {
event.preventDefault();
}
} Defensive patterns
Strategy: type-guard
Validate before calling
import {EventPhase} from '@angular/core/primitives/event-dispatch';
const isReplay = (event: Event): boolean => event.eventPhase === EventPhase.REPLAY; // 101
if (!isReplay(event)) event.preventDefault(); Type guard
import {EventPhase} from '@angular/core/primitives/event-dispatch';
const isReplayed = (e: Event): boolean => e.eventPhase === EventPhase.REPLAY;
// usage in a handler
const maybePreventDefault = (e: Event): void => {
if (!isReplayed(e)) e.preventDefault();
}; Prevention
- Never assume replayed events behave like natively dispatched ones; check eventPhase first
- Suppress default actions during native dispatch (capture listener), not during replay
- Encapsulate preventDefault calls behind one helper that gates on EventPhase.REPLAY
When it happens
Trigger: A delegated listener/handler that calls event.preventDefault() is invoked during event replay — i.e., the event was captured by the EventContract while the dispatcher was not ready (app startup, hydration) and is being replayed afterwards.
Common situations: Handlers written for native addEventListener that call preventDefault() to suppress link/button defaults are reused with the event-dispatch primitive; hydration-time clicks replayed after application bootstrap.
Related errors
AI-assisted analysis of angular/angular@51cb07e980 (2026-08-22).
Data as JSON: /api/errors/fd7a829cf5677d60.
Report an issue: GitHub.