{"record":{"id":"c63f7fcd88bc6023","repo":"octobercms/october","slug":"control-is-not-registered-identifier","errorCode":null,"errorMessage":"Control is not registered [${identifier}]","messagePattern":"Control is not registered \\[(.+?)\\]","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"modules/system/assets/js/framework-bundle.js","lineNumber":4366,"sourceCode":"    register(identifier, controlConstructor) {\n      this.load({ identifier, controlConstructor });\n    }\n    observe(element, identifier) {\n      const observer = this.container.scopeObserver;\n      observer.elementMatchedValue(element, observer.parseValueForToken({\n        element,\n        content: identifier\n      }));\n      const foundControl = this.getControlForElementAndIdentifier(element, identifier);\n      if (!element.matches(`[data-control~=\"${identifier}\"]`)) {\n        element.dataset.control = ((element.dataset.control || \"\") + \" \" + identifier).trim();\n      }\n      return foundControl;\n    }\n    import(identifier) {\n      const module = this.container.getModuleForIdentifier(identifier);\n      if (!module) {\n        throw new Error(`Control is not registered [${identifier}]`);\n      }\n      return module.controlConstructor;\n    }\n    fetch(element, identifier) {\n      if (typeof element === \"string\") {\n        element = document.querySelector(element);\n      }\n      if (!identifier) {\n        identifier = element.dataset.control;\n      }\n      return element ? this.getControlForElementAndIdentifier(element, identifier) : null;\n    }\n    fetchAll(elements, identifier) {\n      if (typeof elements === \"string\") {\n        elements = document.querySelectorAll(elements);\n      }\n      const result = [];\n      elements.forEach((element) => {","sourceCodeStart":4348,"sourceCodeEnd":4384,"githubUrl":"https://github.com/octobercms/october/blob/b608633a7e8922487d91a8161499020121c3b3bf/modules/system/assets/js/framework-bundle.js#L4348-L4384","documentation":"Thrown by Application.import(identifier) in the observe (controls) layer of October CMS's larajax framework, exposed as oc.importControl(identifier). Controls used through data-control=\"...\" attributes must first be registered with oc.registerControl(identifier, controlConstructor) (or app.register); import() looks the identifier up in the module container and throws when nothing was ever registered under that name.","triggerScenarios":"Calling oc.importControl('dropdown') before any oc.registerControl('dropdown', Dropdown) ran; a data-control=\"myControl\" element on the page while the plugin bundle that registers 'myControl' failed to load (404, JS error earlier in the bundle, wrong build); identifier case mismatch ('DropDown' vs 'dropdown').","commonSituations":"Plugin JS not loaded or loaded after the code that imports the control; registering in one bundle but importing from another with different timing; renaming a control class and forgetting the attribute/registration side; disabling a plugin whose script was expected to register the control.","solutions":["Register the control before importing: oc.registerControl('myControl', MyControlClass)","Check the Network/Console tabs: if the plugin's JS bundle 404s or throws, the registration never happens - fix the bundle first","Make sure identifiers match exactly (case-sensitive) between oc.registerControl, oc.importControl, and data-control attributes","For optional/defensive lookups use oc.fetchControl(element, identifier) which returns null instead of throwing"],"exampleFix":"// before\nconst Ctl = oc.importControl('rich-editor'); // throws if never registered\n\n// after\noc.registerControl('rich-editor', RichEditor);\nconst Ctl = oc.importControl('rich-editor');","handlingStrategy":"try-catch","validationCode":"function safeImportControl(identifier) {\n  try {\n    return oc.importControl(identifier);\n  } catch {\n    return null; // not registered (bundle missing / not yet loaded)\n  }\n}","typeGuard":"const controlIsReachable = (el, identifier) => !!oc.fetchControl(el, identifier); // fetchControl returns null instead of throwing","tryCatchPattern":"try {\n  const Ctl = oc.importControl('rich-editor');\n} catch (e) {\n  if (/Control is not registered/.test(e.message)) {\n    console.error(`Register '${'rich-editor'}' via oc.registerControl before importing`);\n  } else throw e;\n}","preventionTips":["Register controls (oc.registerControl) in the same bundle that imports them, before the import runs","Watch the Network tab for failed plugin bundles - a 404 means no registration happened","Keep identifier casing identical across registerControl, importControl and data-control attributes","Use oc.fetchControl for optional lookups - it returns null rather than throwing"],"tags":["controls","registration","ajax","frontend","data-control"],"backgroundTag":"control-not-registered","analyzedSha":"b608633a7e8922487d91a8161499020121c3b3bf","analyzedAt":"2026-08-21T04:24:57.515Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}