{"record":{"id":"e5d1427c03734045","repo":"usablica/intro.js","slug":"provided-callback-for-onexit-was-not-a-function","errorCode":null,"errorMessage":"Provided callback for onexit was not a function.","messagePattern":"Provided callback for onexit was not a function\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/packages/tour/tour.ts","lineNumber":622,"sourceCode":"\n    this.callbacks.start = callback;\n    return this;\n  }\n\n  /**\n   * @deprecated onexit is deprecated, please use onExit instead.\n   */\n  onexit(callback: introExitCallback) {\n    return this.onExit(callback);\n  }\n\n  /**\n   * Add a callback to be called when the tour is exited\n   * @param {Function} callback callback function to be called\n   */\n  onExit(callback: introExitCallback) {\n    if (!isFunction(callback)) {\n      throw new Error(\"Provided callback for onexit was not a function.\");\n    }\n\n    this.callbacks.exit = callback;\n    return this;\n  }\n\n  /**\n   * @deprecated onskip is deprecated, please use onSkip instead.\n   */\n  onskip(callback: introSkipCallback) {\n    return this.onSkip(callback);\n  }\n\n  /**\n   * Add a callback to be called when the tour is skipped\n   * @param {Function} callback callback function to be called\n   */\n  onSkip(callback: introSkipCallback) {","sourceCodeStart":604,"sourceCodeEnd":640,"githubUrl":"https://github.com/usablica/intro.js/blob/e5517e6a24e0530f87885af9fb2a69bafd1d4a6b/src/packages/tour/tour.ts#L604-L640","documentation":"Tour class onExit registers the callback fired when the tour is exited, stored in this.callbacks.exit. It throws this error when the argument is not a function, consistent with all intro.js callback setters.","triggerScenarios":"introJs().onExit() with no argument or any non-function value — commonly undefined from an incorrectly destructured prop or a cleanup function that was already called/removed.","commonSituations":"React/Vue unmount logic removing the handler variable before intro setup runs; passing window event wrapper results; mismatched casing (onexit vs onExit) referencing different wrappers.","solutions":["Pass a function: introJs().onExit(function(){ ... }).","Fix the undefined/non-function source (prop name, import, assignment order).","Ensure registration happens before any code that clears the handler.","Provide a default: introJs().onExit(cfg.onExit || (() => {})).","Guard the registration with typeof === 'function'."],"exampleFix":"// before\nlet cleanupCb;\n// ... cleanupCb = null by the time setup runs\nintroJs().onExit(cleanupCb);\n// after\nintroJs().onExit(function cleanup() { /* restore state */ });","handlingStrategy":"type-guard","validationCode":"if (typeof exitCb === 'function') {\n  introJs().onExit(exitCb);\n} else {\n  introJs().onExit(() => {});\n}","typeGuard":"const isExitCb = (v: unknown): v is () => void => typeof v === 'function';","tryCatchPattern":"try {\n  intro().onExit(cb);\n} catch (e) {\n  if (String(e.message).includes('onexit was not a function')) {\n    intro().onExit(() => {});\n  } else { throw e; }\n}","preventionTips":["Register before any cleanup code nulls the handler","Don't share one callback variable across multiple intro instances","Check destructured props for undefined before use","Prefer explicit inline functions over indirect references"],"tags":["callbacks","type-validation","api-misuse"],"backgroundTag":"callback-not-a-function","analyzedSha":"e5517e6a24e0530f87885af9fb2a69bafd1d4a6b","analyzedAt":"2026-08-31T22:12:26.007Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}