{"record":{"id":"6e6a7260d81bb4f7","repo":"jsdom/jsdom","slug":"constructor-argument-is-not-a-constructor","errorCode":null,"errorMessage":"Constructor argument is not a constructor.","messagePattern":"Constructor argument is not a constructor\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/jsdom/living/custom-elements/CustomElementRegistry-impl.js","lineNumber":71,"sourceCode":"}\n\n// https://html.spec.whatwg.org/#customelementregistry\nclass CustomElementRegistryImpl {\n  constructor(globalObject) {\n    this._customElementDefinitions = [];\n    this._elementDefinitionIsRunning = false;\n    this._whenDefinedPromiseMap = Object.create(null);\n\n    this._globalObject = globalObject;\n  }\n\n  // https://html.spec.whatwg.org/#dom-customelementregistry-define\n  define(name, constructor, options) {\n    const { _globalObject } = this;\n    const ctor = constructor.objectReference;\n\n    if (!isConstructor(ctor)) {\n      throw new TypeError(\"Constructor argument is not a constructor.\");\n    }\n\n    if (!isValidCustomElementName(name)) {\n      throw DOMException.create(_globalObject, [\"Name argument is not a valid custom element name.\", \"SyntaxError\"]);\n    }\n\n    const nameAlreadyRegistered = this._customElementDefinitions.some(entry => entry.name === name);\n    if (nameAlreadyRegistered) {\n      throw DOMException.create(_globalObject, [\n        \"This name has already been registered in the registry.\",\n        \"NotSupportedError\"\n      ]);\n    }\n\n    const ctorAlreadyRegistered = this._customElementDefinitions.some(entry => entry.objectReference === ctor);\n    if (ctorAlreadyRegistered) {\n      throw DOMException.create(_globalObject, [\n        \"This constructor has already been registered in the registry.\",","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/jsdom/jsdom/blob/904cc9cd2464e959b739910cb046afe57f7a1922/lib/jsdom/living/custom-elements/CustomElementRegistry-impl.js#L53-L89","documentation":"customElements.define(name, constructor) requires its second argument to be a callable constructible function/class whose [[Construct]] is usable as a custom element. jsdom checks `isConstructor(ctor)` before anything else and throws this TypeError when the argument is not a real constructor. Since jsdom wraps window objects, this can also happen when a class from a different realm/window is passed.","triggerScenarios":"Calling customElements.define('x-foo', notAClass) — e.g. passing an object literal, an arrow function (not constructor-capable), a bound function, undefined/null, or a class defined in a different jsdom window realm via `new JSDOM().window.customElements.define(...)` with a class from the outer Node realm.","commonSituations":"Typo passing options object as the second argument; spreading define(name, ...args) incorrectly; passing a decorator-produced value; cross-realm class (class defined in Node vs inside the jsdom window) which fails jsdom's constructor check.","solutions":["Ensure the second argument is a real class: `customElements.define('x-foo', class extends HTMLElement {...})`.","If the class lives outside the jsdom window, run the define call inside the window's script context (e.g. `window.eval` or `<script>` inside the JSDOM instance) so the class belongs to the same realm and extends the window's HTMLElement.","Verify with `typeof C === 'function' && C.prototype && C.prototype.constructor === C` before calling define.","Check argument order — a valid constructor in the wrong slot (e.g. define(class, 'x-foo')) will also trigger this."],"exampleFix":"// before\ncustomElements.define('x-foo', { connectedCallback() {} });\n// after\ncustomElements.define('x-foo', class extends window.HTMLElement { connectedCallback() {} });","handlingStrategy":"type-guard","validationCode":"function isRealConstructor(v) {\n  return typeof v === 'function' && typeof v.prototype === 'object' && v.prototype !== null && /^class\\s|[A-Za-z]+\\s*\\{/.test(Function.prototype.toString.call(v).slice(0, 200)) && (() => { try { Reflect.construct(function(){}, [], v); return true; } catch { return false; } })();\n}\nif (!isRealConstructor(C)) throw new TypeError('define requires a class constructor');","typeGuard":"const isClass = (v) => typeof v === 'function' &&\n  (() => { try { new Proxy(v, { construct() { return {}; } }); return true; } catch { return false; } })() &&\n  !Object.prototype.hasOwnProperty.call(v, 'call');","tryCatchPattern":"try {\n  window.customElements.define(name, C);\n} catch (e) {\n  if (e instanceof TypeError && e.message.includes('not a constructor')) {\n    // fall back to defining inside the window realm via window.eval\n  } else { throw e; }\n}","preventionTips":["Always extend the HTMLElement from the same window realm the registry belongs to.","Never pass arrow functions or object literals to customElements.define.","Keep define calls inside the jsdom window context (window.eval or inline <script>)."],"tags":["custom-elements","typeerror","dom-api"],"backgroundTag":"custom-element-define-invalid-constructor","analyzedSha":"904cc9cd2464e959b739910cb046afe57f7a1922","analyzedAt":"2026-09-01T11:17:47.630Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}