angular/angular · error · DOMException
InvalidStateError
InvalidStateError
Error message
Cannot use precommitHandler when cancelable is 'false'
What it means
Calling event.intercept({precommitHandler: ...}) throws a DOMException with name 'InvalidStateError' when the NavigateEvent's cancelable flag is false. The precommit handler runs before commit and can cancel or redirect the navigation, which is only meaningful for cancelable navigations; non-cancelable navigations (as determined by who initiated them) reject this option per the spec step 'Cannot use precommitHandler when cancelable is false'.
Source
Thrown at packages/core/primitives/dom-navigation/testing/fake_navigation.ts:868
event.sameDocument = sameDocument;
let precommitHandlers: Array<(controller: NavigationPrecommitController) => Promise<void>> = [];
let handlers: Array<() => PromiseLike<void> | void> = [];
// https://whatpr.org/html/10919/nav-history-apis.html#dom-navigateevent-intercept
event.intercept = function (
this: InternalFakeNavigateEvent,
options?: ExperimentalNavigationInterceptOptions,
): void {
if (!this.canIntercept) {
throw new DOMException(`Cannot intercept when canIntercept is 'false'`, 'SecurityError');
}
this.interceptionState = 'intercepted';
event.sameDocument = true;
const precommitHandler = options?.precommitHandler;
if (precommitHandler) {
if (!this.cancelable) {
throw new DOMException(
`Cannot use precommitHandler when cancelable is 'false'`,
'InvalidStateError',
);
}
precommitHandlers.push(precommitHandler);
}
if (event.interceptionState !== 'none' && event.interceptionState !== 'intercepted') {
throw new Error('Event interceptionState should be "none" or "intercepted"');
}
event.interceptionState = 'intercepted';
const handler = options?.handler;
if (handler) {
handlers.push(handler);
}
// override old options with new ones. UA _may_ report a console warning if new options differ from previous
event.focusResetBehavior = options?.focusReset ?? event.focusResetBehavior;
event.scrollBehavior = options?.scroll ?? event.scrollBehavior;
};View on GitHub (pinned to 51cb07e980)
Solutions
- Check event.cancelable before passing precommitHandler: e.intercept(e.cancelable ? {precommitHandler, handler} : {handler})
- Move logic that must always run into the regular `handler`, which is allowed for any intercepted navigation
- If the precommit behavior (cancel/redirect) is essential, initiate the navigation yourself via navigation.navigate() so the event is cancelable
Example fix
// before
event.intercept({
precommitHandler: async (c) => { /* ... */ },
handler: async () => { /* ... */ },
}); // InvalidStateError when cancelable === false
// after
event.intercept({
precommitHandler: event.cancelable ? async (c) => { /* ... */ } : undefined,
handler: async () => { /* ... */ },
}); Defensive patterns
Strategy: type-guard
Validate before calling
e.intercept({
precommitHandler: e.cancelable ? myPrecommit : undefined,
handler,
}); Type guard
const canPrecommit = (e: NavigateEvent): boolean => e.cancelable === true;
Try / catch
try {
e.intercept({precommitHandler, handler});
} catch (err) {
if (err instanceof DOMException && err.name === 'InvalidStateError') {
e.intercept({handler}); // retry without the precommitHandler
} else throw err;
} Prevention
- Only pass precommitHandler when event.cancelable is true
- Keep logic that must run for every navigation in the regular handler
- Feature-detect the experimental precommit option per event, not once at startup
When it happens
Trigger: Passing a precommitHandler (the experimental pre-commit interception option) to intercept() on a NavigateEvent where cancelable === false, e.g., navigations initiated by the user agent or explicitly non-cancelable navigate() calls.
Common situations: Using the experimental precommit/redirect Navigation API surface in a listener attached to all navigations; upgrading code that only used `handler` and assuming the new option is always accepted.
Related errors
AI-assisted analysis of angular/angular@51cb07e980 (2026-08-22).
Data as JSON: /api/errors/4e31373f2b52355d.
Report an issue: GitHub.