{"record":{"id":"bbce81d85463f61b","repo":"expressjs/multer","slug":"expected-streamhandler-to-be-a-function","errorCode":null,"errorMessage":"Expected streamHandler to be a function","messagePattern":"Expected streamHandler to be a function","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"index.js","lineNumber":102,"sourceCode":"    this.storage = options.storage\n  } else if (options.dest) {\n    this.storage = diskStorage({ destination: options.dest })\n  } else {\n    this.storage = memoryStorage()\n  }\n\n  if (options.limits && typeof options.limits !== 'function') validateLimits(options.limits)\n\n  this.limits = options.limits\n  this.preservePath = options.preservePath\n  this.defParamCharset = options.defParamCharset || 'latin1'\n  this.defCharset = options.defCharset\n  this.highWaterMark = options.highWaterMark\n  this.fileHwm = options.fileHwm\n  this.fileFilter = options.fileFilter || allowAll\n\n  if (options.streamHandler !== undefined && typeof options.streamHandler !== 'function') {\n    throw new TypeError('Expected streamHandler to be a function')\n  }\n\n  this.streamHandler = options.streamHandler\n}\n\nMulter.prototype._makeMiddleware = function (fields, fileStrategy) {\n  function setup () {\n    var fileFilter = this.fileFilter\n    var filesLeft = Object.create(null)\n\n    fields.forEach(function (field) {\n      if (typeof field.maxCount === 'number') {\n        filesLeft[field.name] = field.maxCount\n      } else {\n        filesLeft[field.name] = Infinity\n      }\n    })\n","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/expressjs/multer/blob/35979e5afbb814bdb4b750ce028b125eb84c53af/index.js#L84-L120","documentation":"Multer (as constructed here) accepts an optional `streamHandler` option that must be a function if provided. During configuration parsing (index.js:102) it explicitly checks `options.streamHandler !== undefined && typeof options.streamHandler !== 'function'` and throws a TypeError immediately, so a misconfigured options object fails fast rather than failing later when the handler is invoked. This is an input-validation guard on the constructor options surface.","triggerScenarios":"Passing `streamHandler` in the Multer options object with a non-function value: e.g. `multer({ streamHandler: true })`, `multer({ streamHandler: 'myHandler' })`, `multer({ streamHandler: null })` (null !== undefined so it is checked), or referencing an undefined-yet-typed/wrong import such as `streamHandler: myModule.handler` where handler is an object or undefined-then-assigned wrong type. Only a value of exactly `undefined` (or omitting the key) is accepted as 'not provided'.","commonSituations":"Copy-pasting configuration from docs where the option was renamed; passing a handler name string instead of the function reference; requiring a handler module and forgetting the property is a default export wrapper; typos where `streamHandler` collides with another string-typed option; JSON-loaded config objects where functions cannot be represented so values arrive as strings; upgrading Multer versions where streamHandler was newly added and old code passed a string/boolean flag.","solutions":["Ensure `streamHandler` is only set in options when you have an actual function, e.g. `multer({ streamHandler: myHandler })` where `typeof myHandler === 'function'`","Remove the `streamHandler` key entirely (or set it to undefined) if you do not intend to customize stream handling — the library defaults it to undefined","If the handler comes from config or an import, check `typeof handler === 'function'` before constructing Multer, and fix the import (use the module's function export, not the module object)","If the value comes from JSON config, resolve it to a function in code (e.g. a lookup map from name to function) instead of passing the raw string"],"exampleFix":"// before\nconst handler = require('./stream-handler'); // module object, not a function\nconst upload = multer({ streamHandler: handler });\n// throws: Expected streamHandler to be a function\n\n// after\nconst { streamHandler } = require('./stream-handler'); // destructure the function\nconst upload = multer({ streamHandler: streamHandler });\n// or simply omit it:\n// const upload = multer({});","handlingStrategy":"validation","validationCode":"function buildMulterOptions(options = {}) {\n  if (options.streamHandler !== undefined && typeof options.streamHandler !== 'function') {\n    throw new TypeError(\n      `streamHandler must be a function or undefined, got ${typeof options.streamHandler}`\n    );\n  }\n  return options;\n}\nconst upload = multer(buildMulterOptions(myOptions));","typeGuard":"function isStreamHandlerOptions(options) {\n  return (\n    options.streamHandler === undefined ||\n    typeof options.streamHandler === 'function'\n  );\n}\n\nif (!isStreamHandlerOptions(myOptions)) {\n  throw new TypeError('streamHandler must be a function if provided');\n}","tryCatchPattern":"try {\n  upload = multer(options);\n} catch (err) {\n  if (err instanceof TypeError && /streamHandler/.test(err.message)) {\n    console.error('Bad streamHandler option; expected a function, got:', typeof options.streamHandler);\n    upload = multer({ ...options, streamHandler: undefined });\n  } else {\n    throw err;\n  }\n}","preventionTips":["Never pass a bare string/boolean for streamHandler — always pass the function reference itself","Destructure function exports from handler modules instead of passing the module object","Validate the whole options object with a schema (e.g. a tiny check function or zod with a custom function validator) before constructing Multer","Only include the streamHandler key in the options object when a handler is actually configured (delete the key otherwise)","Pin and read the Multer version's option docs when upgrading, since option types can change between versions"],"tags":["type-mismatch","options-validation","constructor-argument","multer"],"backgroundTag":"invalid-argument-value","analyzedSha":"35979e5afbb814bdb4b750ce028b125eb84c53af","analyzedAt":"2026-09-20T10:49:39.965Z","contentChangedAt":"2026-09-20T10:49:39.965Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}