{"record":{"id":"f4e3df92f736df82","repo":"usablica/intro.js","slug":"provided-callback-for-onchange-was-not-a-function","errorCode":null,"errorMessage":"Provided callback for onChange was not a function.","messagePattern":"Provided callback for onChange was not a function\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/packages/tour/tour.ts","lineNumber":542,"sourceCode":"\n    this.callbacks.beforeChange = callback;\n    return this;\n  }\n\n  /**\n   * @deprecated onchange is deprecated, please use onChange instead.\n   */\n  onchange(callback: introChangeCallback) {\n    this.onChange(callback);\n  }\n\n  /**\n   * Add a callback to be called when the tour changes steps\n   * @param {Function} callback callback function to be called\n   */\n  onChange(callback: introChangeCallback) {\n    if (!isFunction(callback)) {\n      throw new Error(\"Provided callback for onChange was not a function.\");\n    }\n\n    this.callbacks.change = callback;\n    return this;\n  }\n\n  /**\n   * @deprecated onafterchange is deprecated, please use onAfterChange instead.\n   */\n  onafterchange(callback: introAfterChangeCallback) {\n    this.onAfterChange(callback);\n  }\n\n  /**\n   * Add a callback to be called after the tour changes steps\n   * @param {Function} callback callback function to be called\n   */\n  onAfterChange(callback: introAfterChangeCallback) {","sourceCodeStart":524,"sourceCodeEnd":560,"githubUrl":"https://github.com/usablica/intro.js/blob/e5517e6a24e0530f87885af9fb2a69bafd1d4a6b/src/packages/tour/tour.ts#L524-L560","documentation":"Tour class onChange registers the per-step-change callback into this.callbacks.change. It validates the argument with isFunction and throws when it is not a function, so the tour's internal call site can invoke it unconditionally.","triggerScenarios":"introJs().onChange() with no argument or a non-function value (undefined variable, Promise, object) — commonly from an options object wired to the wrong key.","commonSituations":"Passing the result of an async function (a Promise) instead of a function; SSR frameworks where the handler module isn't loaded; renaming handlers and missing one call site.","solutions":["Pass a function: introJs().onChange(function(targetElement){ ... }).","Verify typeof the argument is 'function' right before the call and trace back why it isn't.","Register callbacks after module/config initialization completes.","Use a no-op fallback: introJs().onChange(config.onChange || function(){}).","Catch the error to log which setup step was misconfigured."],"exampleFix":"// before\nintroJs().onChange(await loadHandler()); // Promise, not function\n// after\nconst handler = await loadHandler();\nintroJs().onChange(() => handler());","handlingStrategy":"validation","validationCode":"if (typeof changeCb !== 'function') changeCb = () => {};\nintroJs().onChange(changeCb);","typeGuard":"function isFunction(v: unknown): v is Function { return typeof v === 'function'; }","tryCatchPattern":"try {\n  introJs().onChange(cb);\n} catch (e) {\n  if (/onChange was not a function/.test(e.message)) {\n    introJs().onChange(() => {});\n  } else { throw e; }\n}","preventionTips":["Pass functions, never Promises or their results","Assign handlers before introJs() setup runs","Use consistent naming between config keys and variables","Lint for calls to onChange/on* with non-identifier arguments"],"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"}