{"record":{"id":"976fc236e7cef0c2","repo":"octobercms/october","slug":"getdynamicoptionsextradata-must-return-an-object","errorCode":null,"errorMessage":"getDynamicOptionsExtraData must return an object","messagePattern":"getDynamicOptionsExtraData must return an object","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"modules/backend/vuecomponents/inspector/assets/js/classes/control-base.js","lineNumber":123,"sourceCode":"\n        setManagedValue: function setManagedValue(value) {\n            utils.setProperty(this.obj, this.control.property, value);\n        },\n\n        refreshDisplayedValue: function refreshDisplayedValue() { },\n\n        focusControl: function focusControl() { },\n\n        inspectorGetTab: function inspectorGetTab() {\n            return this.control.tab;\n        },\n\n        loadDynamicOptions: function loadDynamicOptions() {\n            var data = Object.assign({}, $.oc.vueUtils.getCleanObject(this.parentObj), $.oc.vueUtils.getCleanObject(this.obj));\n            if (typeof this.control.getDynamicOptionsExtraData === 'function') {\n                const extraData = this.control.getDynamicOptionsExtraData();\n                if (typeof extraData !== 'object') {\n                    throw new Error('getDynamicOptionsExtraData must return an object');\n                }\n\n                Object.assign(data, extraData);\n            }\n\n            this.loadingDynamicOptions = true;\n\n            data['inspectorProperty'] = this.control.property;\n            data['inspectorClassName'] = this.serverClassName;\n\n            var handlerAlias = this.inspectorPreferences.handlerAlias,\n                optionsHandler = handlerAlias\n                    ? handlerAlias + '::onInspectableGetOptions'\n                    : 'onInspectableGetOptions';\n\n            return dataLoader.requestOptions(\n                    this.$el,\n                    this.serverClassName,","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/octobercms/october/blob/b608633a7e8922487d91a8161499020121c3b3bf/modules/backend/vuecomponents/inspector/assets/js/classes/control-base.js#L105-L141","documentation":"loadDynamicOptions() in the Inspector control base merges extra request data from the optional control hook getDynamicOptionsExtraData(). If a custom control defines that hook but returns something that is not an object (string, number, boolean, undefined), it throws before the AJAX request is sent. The check is typeof-based, so null technically passes but contributes nothing.","triggerScenarios":"A custom inspector control defines getDynamicOptionsExtraData() that returns a string (e.g. serialized data), a boolean, or has an early-exit path with no return (undefined); the control then triggers a dynamic-options load.","commonSituations":"Custom inspector controls with dynamicOptions enabled where the hook has a conditional return and no default; returning an array of pairs instead of an object; returning a JSON.stringify result instead of the parsed object.","solutions":["Make the hook always return an object literal — add `return {};` when there is nothing to add.","Check for early returns or missing return statements in the hook.","Return parsed objects, never JSON strings."],"exampleFix":"// before\ngetDynamicOptionsExtraData: function () {\n    if (this.contextTab) {\n        return { tab: this.contextTab };\n    }\n}\n\n// after\ngetDynamicOptionsExtraData: function () {\n    if (this.contextTab) {\n        return { tab: this.contextTab };\n    }\n\n    return {};\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function extraDataIsObject(value) {\n    return value === null || typeof value === 'object';\n}\n\n// inside the control, normalize before returning\ngetDynamicOptionsExtraData: function () {\n    var extra = this.computeExtra();\n    return extraDataIsObject(extra) ? extra : {};\n}","tryCatchPattern":null,"preventionTips":["Always end getDynamicOptionsExtraData() with `return {};`","Return parsed objects, never JSON strings"],"tags":["inspector","vue","custom-control","return-type"],"backgroundTag":"invalid-return-type","analyzedSha":"b608633a7e8922487d91a8161499020121c3b3bf","analyzedAt":"2026-08-21T04:24:57.515Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}