{"record":{"id":"fd3a2a8b03cebc80","repo":"octobercms/october","slug":"trying-to-get-selected-row-without-a-popup-referen","errorCode":null,"errorMessage":"Trying to get selected row without a popup reference.","messagePattern":"Trying to get selected row without a popup reference\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"modules/backend/assets/foundation/controls/inspector/inspector.editor.objectlist.js","lineNumber":351,"sourceCode":"            return\n        }\n\n        value = $.trim(value)\n\n        if (value.length === 0) {\n            value = '[No title]'\n            $.oc.foundation.element.addClass(selectedRow, 'disabled')\n        }\n        else {\n            $.oc.foundation.element.removeClass(selectedRow, 'disabled')\n        }\n\n        selectedRow.firstChild.textContent = value\n    }\n\n    ObjectListEditor.prototype.getSelectedRow = function() {\n        if (!this.popup) {\n            throw new Error('Trying to get selected row without a popup reference.')\n        }\n\n        var rows = this.getTableBody().children\n\n        for (var i = 0, len = rows.length; i < len; i++) {\n            if ($.oc.foundation.element.hasClass(rows[i], 'active')) {\n                return rows[i]\n            }\n        }\n\n        return null\n    }\n\n    ObjectListEditor.prototype.createItem = function() {\n        var selectedRow = this.getSelectedRow()\n\n        if (selectedRow) {\n            if (!this.validateKeyValue()) {","sourceCodeStart":333,"sourceCodeEnd":369,"githubUrl":"https://github.com/octobercms/october/blob/b608633a7e8922487d91a8161499020121c3b3bf/modules/backend/assets/foundation/controls/inspector/inspector.editor.objectlist.js#L333-L369","documentation":"The dashboard inspector-configurator fetches dropdown options over AJAX (via the handler named by the store's `onInspectableGetOptions` event) and requires the response's `options` property to be an array of `{ value, title }` items, which it converts into a value-to-title map. If the server handler returns `options` as an object/map, a string, or null (for example by returning `['one' => 'One']` keyed maps directly), Array.isArray fails and this Error is thrown client-side.","triggerScenarios":"A custom `onInspectableGetOptions` handler returning a key=>title map instead of `['options' => [ ['value' => ..., 'title' => ...], ... ]]`; a handler that returns nothing (null) or an error page/HTML because it threw; the request being intercepted and reshaped by other JS.","commonSituations":"Porting old get*Options methods (which return `['value' => 'title']` maps, like Backend's InspectableContainer converts them) and forgetting the conversion; dashboard widgets implementing HasPropertyOptions::onInspectableGetOptions incorrectly; handlers crashing so the AJAX response has no options key.","solutions":["Make the handler return `['options' => [...]]` where the inner value is a list of items with `value` and `title` keys.","If your source data is a value=>title map, convert it: `foreach ($map as $value => $title) { $list[] = ['value' => $value, 'title' => $title]; } return ['options' => $list];`.","Check the network tab for a non-200 or error response — an exception inside the handler also produces a missing/non-array `options`."],"exampleFix":"// before\npublic function onInspectableGetOptions()\n{\n    return ['one' => 'One', 'two' => 'Two'];\n}\n\n// after\npublic function onInspectableGetOptions()\n{\n    $list = [];\n    foreach (['one' => 'One', 'two' => 'Two'] as $value => $title) {\n        $list[] = ['value' => $value, 'title' => $title];\n    }\n    return ['options' => $list];\n}","handlingStrategy":"type-guard","validationCode":"// Server-side: normalize any map into the required options array shape\npublic function onInspectableGetOptions()\n{\n    $map = $this->getMyOptions(); // ['one' => 'One', ...]\n    $list = [];\n    foreach ($map as $value => $title) {\n        $list[] = ['value' => (string) $value, 'title' => $title];\n    }\n    return ['options' => $list];\n}","typeGuard":"// Client-side guard around the response before using it\n/**\n * @param {any} data\n * @returns {boolean}\n */\nfunction isValidOptionsResponse(data) {\n    return data != null\n        && Array.isArray(data.options)\n        && data.options.every(item => item != null && 'value' in item && 'title' in item);\n}","tryCatchPattern":"try {\n    const result = await this.requestOptions(/* ... */);\n} catch (e) {\n    if (/must return an array/.test(e.message)) {\n    // inspect the handler response in the network tab; fix the PHP return shape\n    }\n}","preventionTips":["Handler contract: return ['options' => [ ['value' => ..., 'title' => ...], ... ]] — never a bare map or null.","Copy the conversion loop from Backend\\Traits\\InspectableContainer when your source is value=>title pairs.","Watch the network tab first; an exceptioning handler also yields a non-array options."],"tags":["javascript","october-cms","dashboard","inspector","ajax","response-shape"],"backgroundTag":"invalid-response-shape","analyzedSha":"b608633a7e8922487d91a8161499020121c3b3bf","analyzedAt":"2026-08-21T04:24:57.515Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}