{"record":{"id":"fd7a829cf5677d60","repo":"angular/angular","slug":"preventdefault-called-during-event-replay","errorCode":null,"errorMessage":"`preventDefault` called during event replay.","messagePattern":"`preventDefault` called during event replay\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/primitives/event-dispatch/src/event_dispatcher.ts","lineNumber":129,"sourceCode":"  };\n  patchEventInstance(event, 'stopPropagation', stopPropagation);\n  patchEventInstance(event, 'stopImmediatePropagation', stopPropagation);\n}\n\nfunction propagationStopped(eventInfoWrapper: EventInfoWrapper) {\n  const event = eventInfoWrapper.getEvent();\n  return !!event[PROPAGATION_STOPPED_SYMBOL];\n}\n\nfunction prepareEventForReplay(eventInfoWrapper: EventInfoWrapper) {\n  const event = eventInfoWrapper.getEvent();\n  const target = eventInfoWrapper.getTargetElement();\n  const originalPreventDefault = event.preventDefault.bind(event);\n  patchEventInstance(event, 'target', target);\n  patchEventInstance(event, 'eventPhase', EventPhase.REPLAY);\n  patchEventInstance(event, 'preventDefault', () => {\n    originalPreventDefault();\n    throw new Error(\n      PREVENT_DEFAULT_ERROR_MESSAGE + (ngDevMode ? PREVENT_DEFAULT_ERROR_MESSAGE_DETAILS : ''),\n    );\n  });\n  patchEventInstance(event, 'composedPath', () => {\n    throw new Error(\n      COMPOSED_PATH_ERROR_MESSAGE + (ngDevMode ? COMPOSED_PATH_ERROR_MESSAGE_DETAILS : ''),\n    );\n  });\n}\n\nfunction prepareEventForDispatch(eventInfoWrapper: EventInfoWrapper) {\n  const event = eventInfoWrapper.getEvent();\n  const currentTarget = eventInfoWrapper.getAction()?.element;\n  if (currentTarget) {\n    patchEventInstance(event, 'currentTarget', currentTarget, {\n      // `currentTarget` is going to get reassigned every dispatch.\n      configurable: true,\n    });","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/angular/angular/blob/51cb07e98081ab7e4e84a9e0949266a8e19cca84/packages/core/primitives/event-dispatch/src/event_dispatcher.ts#L111-L147","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nhandler: (event: Event) => {\n  event.preventDefault(); // throws during replay\n}\n\n// after\nimport {EventPhase} from '@angular/core/primitives/event-dispatch';\nhandler: (event: Event) => {\n  if (event.eventPhase !== EventPhase.REPLAY) {\n    event.preventDefault();\n  }\n}","handlingStrategy":"type-guard","validationCode":"import {EventPhase} from '@angular/core/primitives/event-dispatch';\n\nconst isReplay = (event: Event): boolean => event.eventPhase === EventPhase.REPLAY; // 101\n\nif (!isReplay(event)) event.preventDefault();","typeGuard":"import {EventPhase} from '@angular/core/primitives/event-dispatch';\n\nconst isReplayed = (e: Event): boolean => e.eventPhase === EventPhase.REPLAY;\n\n// usage in a handler\nconst maybePreventDefault = (e: Event): void => {\n  if (!isReplayed(e)) e.preventDefault();\n};","tryCatchPattern":null,"preventionTips":["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"],"tags":["event-dispatch","replay","prevent-default","primitives"],"backgroundTag":"event-replay-restricted-api","analyzedSha":"51cb07e98081ab7e4e84a9e0949266a8e19cca84","analyzedAt":"2026-08-22T07:04:54.531Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}