{"record":{"id":"4e31373f2b52355d","repo":"angular/angular","slug":"invalidstateerror","errorCode":"InvalidStateError","errorMessage":"Cannot use precommitHandler when cancelable is 'false'","messagePattern":"Cannot use precommitHandler when cancelable is 'false'","errorType":"exception","errorClass":"DOMException","httpStatus":null,"severity":"error","filePath":"packages/core/primitives/dom-navigation/testing/fake_navigation.ts","lineNumber":868,"sourceCode":"  event.sameDocument = sameDocument;\n\n  let precommitHandlers: Array<(controller: NavigationPrecommitController) => Promise<void>> = [];\n  let handlers: Array<() => PromiseLike<void> | void> = [];\n\n  // https://whatpr.org/html/10919/nav-history-apis.html#dom-navigateevent-intercept\n  event.intercept = function (\n    this: InternalFakeNavigateEvent,\n    options?: ExperimentalNavigationInterceptOptions,\n  ): void {\n    if (!this.canIntercept) {\n      throw new DOMException(`Cannot intercept when canIntercept is 'false'`, 'SecurityError');\n    }\n    this.interceptionState = 'intercepted';\n    event.sameDocument = true;\n    const precommitHandler = options?.precommitHandler;\n    if (precommitHandler) {\n      if (!this.cancelable) {\n        throw new DOMException(\n          `Cannot use precommitHandler when cancelable is 'false'`,\n          'InvalidStateError',\n        );\n      }\n      precommitHandlers.push(precommitHandler);\n    }\n    if (event.interceptionState !== 'none' && event.interceptionState !== 'intercepted') {\n      throw new Error('Event interceptionState should be \"none\" or \"intercepted\"');\n    }\n    event.interceptionState = 'intercepted';\n    const handler = options?.handler;\n    if (handler) {\n      handlers.push(handler);\n    }\n    // override old options with new ones. UA _may_ report a console warning if new options differ from previous\n    event.focusResetBehavior = options?.focusReset ?? event.focusResetBehavior;\n    event.scrollBehavior = options?.scroll ?? event.scrollBehavior;\n  };","sourceCodeStart":850,"sourceCodeEnd":886,"githubUrl":"https://github.com/angular/angular/blob/51cb07e98081ab7e4e84a9e0949266a8e19cca84/packages/core/primitives/dom-navigation/testing/fake_navigation.ts#L850-L886","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nevent.intercept({\n  precommitHandler: async (c) => { /* ... */ },\n  handler: async () => { /* ... */ },\n}); // InvalidStateError when cancelable === false\n\n// after\nevent.intercept({\n  precommitHandler: event.cancelable ? async (c) => { /* ... */ } : undefined,\n  handler: async () => { /* ... */ },\n});","handlingStrategy":"type-guard","validationCode":"e.intercept({\n  precommitHandler: e.cancelable ? myPrecommit : undefined,\n  handler,\n});","typeGuard":"const canPrecommit = (e: NavigateEvent): boolean => e.cancelable === true;","tryCatchPattern":"try {\n  e.intercept({precommitHandler, handler});\n} catch (err) {\n  if (err instanceof DOMException && err.name === 'InvalidStateError') {\n    e.intercept({handler}); // retry without the precommitHandler\n  } else throw err;\n}","preventionTips":["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"],"tags":["dom-navigation","intercept","invalid-state-error","precommit"],"backgroundTag":"event-intercept-invalid-state","analyzedSha":"51cb07e98081ab7e4e84a9e0949266a8e19cca84","analyzedAt":"2026-08-22T07:04:54.531Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}