{"record":{"id":"fd0484a98b403d80","repo":"swagger-api/swagger-ui","slug":"wrapselector-needs-to-return-a-function-that-retur","errorCode":null,"errorMessage":"wrapSelector needs to return a function that returns a new function (ie the wrapped action)","messagePattern":"wrapSelector needs to return a function that returns a new function \\(ie the wrapped action\\)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/core/system.js","lineNumber":207,"sourceCode":"      return objMap(selectorGroups, (selectors, selectorGroupName) => {\n        let stateName = [selectorGroupName.slice(0, -9)] // selectors = 9 chars\n        let wrappers = this.system.statePlugins[stateName].wrapSelectors\n          if(wrappers) {\n            return objMap(selectors, (selector, selectorName) => {\n              let wrap = wrappers[selectorName]\n              if(!wrap) {\n                return selector\n              }\n\n              if(!Array.isArray(wrap)) {\n                wrap = [wrap]\n              }\n              return wrap.reduce((acc, fn) => {\n                let wrappedSelector = (...args) => {\n                  return fn(acc, this.getSystem())(getState().getIn(stateName), ...args)\n                }\n                if(!isFn(wrappedSelector)) {\n                  throw new TypeError(\"wrapSelector needs to return a function that returns a new function (ie the wrapped action)\")\n                }\n                return wrappedSelector\n              }, selector || Function.prototype)\n            })\n          }\n        return selectors\n      })\n  }\n\n  getStates(state) {\n    return Object.keys(this.system.statePlugins).reduce((obj, key) => {\n      obj[key] = state.get(key)\n      return obj\n    }, {})\n  }\n\n  getStateThunks(getState) {\n    return Object.keys(this.system.statePlugins).reduce((obj, key) => {","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/swagger-api/swagger-ui/blob/3d9d0916d4c0542c50639a9a0e680ab1475683a6/src/core/system.js#L189-L225","documentation":"Plugins extend selectors via statePlugins.<plugin>.wrapSelectors.<selectorName>, and each wrapper must be curried — (originalSelector, getSystem) => (stateSlice, ...args) => newValue — because the system invokes it with the plugin's Immutable state slice (getState().getIn([pluginName])) plus any caller args. This TypeError is raised in getWrappedAndBoundSelectors (src/core/system.js:207) during system construction when the rebuilt selector is not callable. As with wrapActions, a wrapper that forgets the inner arrow more often appears later as a 'TypeError: ... is not a function' at selector invocation.","triggerScenarios":"Defining wrapSelectors with a single arrow returning a value, e.g. wrapSelectors: { isOAS3: (ori, system) => true } instead of (ori, system) => (state, ...args) => true; returning a non-function from the outer arrow; copying a wrapActions-shaped signature into a wrapSelectors slot; getting the argument order wrong so the outer call yields a value.","commonSituations":"Writing a plugin that augments auth or spec selectors and flattening the wrapper to one arrow; copying an OAS31 wrapSelector example but dropping one layer; selector key not matching an existing selector name (silently no-ops).","solutions":["Make every wrapSelectors entry a double arrow: (ori, system) => (state, ...args) => { return ori(state, ...args) }.","Confirm the selector key matches an existing selector in that statePlugin.","Remember the inner function's first argument is the plugin's Immutable state slice, not the whole Redux store."],"exampleFix":"// before\nwrapSelectors: {\n  isOAS3: (ori, system) => true\n}\n\n// after\nwrapSelectors: {\n  isOAS3: (ori, system) => (state, ...args) => {\n    return ori(state, ...args)\n  }\n}","handlingStrategy":"validation","validationCode":"// Validate a plugin's wrapSelectors before registering it.\nfunction validateWrapSelectors(plugin) {\n  const groups = (plugin && plugin.statePlugins) || {}\n  for (const [pluginName, sp] of Object.entries(groups)) {\n    for (const [name, wrap] of Object.entries(sp.wrapSelectors || {})) {\n      const arr = Array.isArray(wrap) ? wrap : [wrap]\n      arr.forEach((fn, i) => {\n        if (typeof fn !== \"function\")\n          throw new Error(`wrapSelectors[${pluginName}.${name}][${i}] is not a function`)\n        const produced = fn(() => {}, {}) // (ori, system)\n        if (typeof produced !== \"function\")\n          throw new Error(`wrapSelectors[${pluginName}.${name}][${i}] must return a function; got ${typeof produced}`)\n      })\n    }\n  }\n  return plugin\n}","typeGuard":"// Narrows a wrapSelector entry to the curried (ori, system) => fn form.\nconst isWrapSelector = (w) => {\n  const fn = Array.isArray(w) ? w[0] : w\n  return typeof fn === \"function\" && typeof fn(() => {}, {}) === \"function\"\n}","tryCatchPattern":"try {\n  SwaggerUI({ plugins: [myPlugin], domNode: \"#swagger\" })\n} catch (e) {\n  if (/wrapSelector needs to return a function/.test(e.message)) {\n    console.error(\"A wrapSelectors entry is not curried (needs two arrow functions):\", e)\n  } else {\n    throw e\n  }\n}","preventionTips":["Always use the two-arrow shape (ori, system) => (state, ...args) => result for wrapSelectors.","Remember the inner first argument is the plugin's Immutable state slice, not the full store.","Model custom wrapSelectors on src/core/plugins/oas31/auth-extensions/wrap-selectors.js."],"tags":["plugins","wrap-selectors","redux","immutable","developer-error","plugin-system"],"backgroundTag":null,"analyzedSha":"3d9d0916d4c0542c50639a9a0e680ab1475683a6","analyzedAt":"2026-08-13T10:03:12.308Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}