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

  1. Guard the call: only invoke preventDefault() when event.eventPhase !== EventPhase.REPLAY (101)
  2. For defaults that must truly be suppressed, handle the event during native dispatch (capture-phase native listener) instead of in the replayed contract handler
  3. 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

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.