{"record":{"id":"617a19353778900f","repo":"NativeScript/NativeScript","slug":"illegal-invocation","errorCode":null,"errorMessage":"Illegal invocation","messagePattern":"Illegal invocation","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/core/media-query-list/index.ts","lineNumber":201,"sourceCode":"\t}\n\n\tpublic set onchange(callback: (this: MediaQueryList, ev: MediaQueryListEvent) => any) {\n\t\tthis._throwInvocationError?.();\n\n\t\t// Remove old listener if any\n\t\tif (this._onChange) {\n\t\t\tthis.removeListener(this._onChange);\n\t\t}\n\n\t\tif (callback) {\n\t\t\tthis.addListener(callback);\n\t\t}\n\n\t\tthis._onChange = callback;\n\t}\n\n\tprivate _throwInvocationError() {\n\t\tthrow new TypeError('Illegal invocation');\n\t}\n}\n\nexport { matchMedia, MediaQueryListImpl as MediaQueryList, checkIfMediaQueryMatches };\n","sourceCodeStart":183,"sourceCodeEnd":206,"githubUrl":"https://github.com/NativeScript/NativeScript/blob/6800aefa6511d23ddad8beb38ccc8541c5e91628/packages/core/media-query-list/index.ts#L183-L206","documentation":"MediaQueryList methods (media, matches, addEventListener, removeEventListener, addListener, removeListener, onchange) throw TypeError('Illegal invocation') when called without a proper `this` - i.e. the method was detached from its instance (same as DOM 'Illegal invocation' errors). _throwInvocationError() is a shared helper enforcing that these methods are invoked on a real MediaQueryList instance created by matchMedia().","triggerScenarios":"Destructuring methods off the instance (`const { matches, addListener } = matchMedia(q)`) then calling them bare; passing the method as a standalone callback (matches.call(undefined)); assigning the method to another object and calling it.","commonSituations":"React/Vue code extracting mql.matches into a variable for reactivity; functional-style code passing mql.addEventListener as a free function; mocks in unit tests that copy methods onto plain objects.","solutions":["Keep the MediaQueryList instance and call methods on it: mql.matches, mql.addListener(cb).","Bind methods: mql.addListener.bind(mql) before detaching.","Wrap in an arrow function when passing as callback: () => mql.matches.","Use mql.addEventListener('change', cb) on the instance instead of extracting handlers."],"exampleFix":"// before\nconst { matches } = matchMedia('(min-width: 600px)');\nconsole.log(matches);\n// after\nconst mql = matchMedia('(min-width: 600px)');\nconsole.log(mql.matches);","handlingStrategy":"type-guard","validationCode":"const mql = matchMedia('(min-width: 500px)');\n// keep a reference; never destructure methods off mql","typeGuard":"function isBoundMql(m) {\n  return m instanceof Object && typeof m.media === 'string' && typeof m.matches === 'boolean';\n}","tryCatchPattern":"try {\n  matches = detachedFn();\n} catch (e) {\n  if (e instanceof TypeError && e.message === 'Illegal invocation') {\n    matches = mql.matches; // call on the instance\n  } else throw e;\n}","preventionTips":["Never destructure methods from MediaQueryList instances.","Use arrow-function wrappers when passing methods as callbacks.","Use .bind(mql) if a detached reference is unavoidable."],"tags":["media-query","this-binding","illegal-invocation"],"backgroundTag":"illegal-invocation","analyzedSha":"6800aefa6511d23ddad8beb38ccc8541c5e91628","analyzedAt":"2026-08-30T20:04:56.809Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}