{"record":{"id":"c691d0be10836f27","repo":"swagger-api/swagger-ui","slug":"wrapactions-needs-to-return-a-function-that-return","errorCode":null,"errorMessage":"wrapActions needs to return a function that returns a new function (ie the wrapped action)","messagePattern":"wrapActions 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":177,"sourceCode":"    let actionGroups = this.getBoundActions(dispatch)\n      return objMap(actionGroups, (actions, actionGroupName) => {\n        let wrappers = this.system.statePlugins[actionGroupName.slice(0,-7)].wrapActions\n          if(wrappers) {\n            return objMap(actions, (action, actionName) => {\n              let wrap = wrappers[actionName]\n              if(!wrap) {\n                return action\n              }\n\n              if(!Array.isArray(wrap)) {\n                wrap = [wrap]\n              }\n              return wrap.reduce((acc, fn) => {\n                let newAction = (...args) => {\n                  return fn(acc, this.getSystem())(...args)\n                }\n                if(!isFn(newAction)) {\n                  throw new TypeError(\"wrapActions needs to return a function that returns a new function (ie the wrapped action)\")\n                }\n                return wrapWithTryCatch(newAction, this.getSystem)\n              }, action || Function.prototype)\n            })\n          }\n        return actions\n      })\n  }\n\n  getWrappedAndBoundSelectors(getState, getSystem) {\n    let selectorGroups = this.getBoundSelectors(getState, getSystem)\n      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) {","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/swagger-api/swagger-ui/blob/3d9d0916d4c0542c50639a9a0e680ab1475683a6/src/core/system.js#L159-L195","documentation":"Swagger UI's plugin system registers action wrappers under statePlugins.<plugin>.wrapActions.<actionName>. Each entry must be a curried higher-order function — (originalAction, getSystem) => (...args) => newValue — so the system can rebuild a callable action. This TypeError is raised in getWrappedAndBoundActions (src/core/system.js:177) during system construction when the rebuilt wrapper is not a function. In practice the guard inspects a locally-built closure, so the same mistake usually surfaces as a downstream 'TypeError: ... is not a function' when the wrapped action is dispatched.","triggerScenarios":"Defining a wrapActions entry with only one arrow that returns a value, e.g. wrapActions: { updateSpec: (ori, system) => { doSomething() } } (returns undefined) instead of (ori, system) => (...args) => {...}; returning a plain value/object from the outer arrow; assigning a wrapActions key to a function whose outer call does not yield a function.","commonSituations":"Writing a custom Swagger UI plugin and forgetting the second (inner) arrow function — the most common copy-paste mistake; migrating a plugin from another Redux app and assuming a simpler (ori) => result signature; assigning the wrapper to a key that does not match an existing action name (silently no-ops, leading devs to 'fix' it by changing the signature incorrectly).","solutions":["Reshape each wrapActions entry as two nested arrow functions: (oriAction, system) => (...args) => { ...; return ori(...args) }.","Confirm the wrapper key exactly matches an existing action name in the same statePlugin (e.g. spec.updateSpec), otherwise the wrap never engages.","Even if you only need the system and no args, still return (...args) => ori(...args) — the inner function is mandatory.","Smoke-test the plugin by constructing SwaggerUI({ plugins: [yourPlugin], domNode }) and dispatching the wrapped action to confirm it stays callable."],"exampleFix":"// before\nwrapActions: {\n  updateSpec: (ori, system) => {\n    console.log(\"wrapping\", system)\n  }\n}\n\n// after\nwrapActions: {\n  updateSpec: (ori, system) => (...args) => {\n    console.log(\"wrapping\", system)\n    return ori(...args)\n  }\n}","handlingStrategy":"validation","validationCode":"// Validate a plugin's wrapActions before registering it.\nfunction validateWrapActions(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.wrapActions || {})) {\n      const arr = Array.isArray(wrap) ? wrap : [wrap]\n      arr.forEach((fn, i) => {\n        if (typeof fn !== \"function\")\n          throw new Error(`wrapActions[${pluginName}.${name}][${i}] is not a function`)\n        const produced = fn(() => {}, {}) // (ori, system)\n        if (typeof produced !== \"function\")\n          throw new Error(`wrapActions[${pluginName}.${name}][${i}] must return a function; got ${typeof produced}`)\n      })\n    }\n  }\n  return plugin\n}","typeGuard":"// Narrows a wrapAction entry to the curried (ori, system) => fn form.\nconst isWrapAction = (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 (/wrapActions needs to return a function/.test(e.message)) {\n    console.error(\"A wrapActions 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) => (...args) => result for wrapActions.","Copy a known-good wrapActions block (e.g. src/core/plugins/on-complete/index.js) as your template.","Run validateWrapActions on custom plugins before passing them to SwaggerUI()."],"tags":["plugins","wrap-actions","redux","developer-error","plugin-system"],"backgroundTag":null,"analyzedSha":"3d9d0916d4c0542c50639a9a0e680ab1475683a6","analyzedAt":"2026-08-13T10:03:12.308Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}