expressjs/multer · error · Error
Unknown file strategy:
Error message
Unknown file strategy:
What it means
Thrown by the FileAppender constructor at lib/file-appender.js:15 when its `strategy` argument is not one of the four recognized constants: 'NONE', 'VALUE', 'ARRAY', or 'OBJECT'. The appender uses the strategy to decide how to shape `req.files` (omit, single value, array, or keyed object) and to insert/remove/replace placeholders during the multipart parse, so an unknown strategy is treated as a programmer error and aborts immediately. The trailing space in 'Unknown file strategy: ' tells you `strategy` stringified to an empty value — i.e. it was `undefined` or `''`. Under normal use this is unreachable: every public method on the Multer prototype (single/array/fields/none/any in index.js) passes a hard-coded valid constant to `_makeMiddleware`, which then forwards it as `options.fileStrategy` to make-middleware.js:25 and finally to `new FileAppender(fileStrategy, req)` at make-middleware.js:143.
Solutions
- Use the documented public API (multer().single(), .array(), .fields(), .none(), .any()) — these always supply a valid strategy constant and cannot reach this branch.
- If you extend Multer, always pass one of the four constants as the second argument to `_makeMiddleware`: 'VALUE' (single file), 'ARRAY' (array/any), 'OBJECT' (fields), 'NONE' (no files).
- If you monkey-patch `_makeMiddleware`, preserve both arguments: `const orig = Multer.prototype._makeMiddleware; Multer.prototype._makeMiddleware = function (f, s) { /* log */ return orig.call(this, f, s); }`.
- If you construct FileAppender directly in a custom storage engine, pass a literal constant from the set above — never a computed/optional value.
- Audit your code for any `delete Multer.prototype.single` / reassignment of the prototype methods that could leave fileStrategy unset.
Example fix
// before (extends Multer but forgets the strategy -> 'Unknown file strategy: ')
Multer.prototype.uploadPair = function (name) {
return this._makeMiddleware([{ name, maxCount: 2 }])
}
// after
Multer.prototype.uploadPair = function (name) {
return this._makeMiddleware([{ name, maxCount: 2 }], 'ARRAY')
} Defensive patterns
Strategy: validation
Validate before calling
const VALID_STRATEGIES = new Set(['NONE', 'VALUE', 'ARRAY', 'OBJECT']);
function assertFileStrategy(strategy) {
if (!VALID_STRATEGIES.has(strategy)) {
throw new Error(
'Invalid file strategy ' + JSON.stringify(strategy) +
'; expected one of ' + Array.from(VALID_STRATEGIES).join(', ')
);
}
}
// before reaching FileAppender / _makeMiddleware:
assertFileStrategy(myStrategy); Type guard
const FILE_STRATEGIES = new Set(['NONE', 'VALUE', 'ARRAY', 'OBJECT']);
function isFileStrategy(s) {
return typeof s === 'string' && FILE_STRATEGIES.has(s);
}
// usage when extending Multer:
// const strat = isFileStrategy(myStrat) ? myStrat : 'VALUE'; Try / catch
// This error fires per-request inside make-middleware.js. A catch is only useful
// to convert it into a clean 500 instead of an unhandled throw.
app.use((err, req, res, next) => {
if (/Unknown file strategy/.test(err && err.message)) {
return res.status(500).json({ error: 'Server file-handling misconfigured' });
}
next(err);
}); Prevention
- Prefer the public API (single/array/fields/none/any) — the strategy constants are wired up correctly there and this branch is unreachable.
- Never call the underscore-prefixed `_makeMiddleware` from application code; treat it as private.
- If you subclass or monkey-patch Multer, write a wrapper that asserts the strategy is one of the four constants before delegating.
- Keep a single source of truth for the strategy constants in your fork and import them rather than typing string literals.
- Add a smoke test that exercises each custom upload method against a real multipart request so a missing strategy fails in CI, not in production.
When it happens
Trigger: Calling the internal `multerInstance._makeMiddleware(fields)` WITHOUT the second `fileStrategy` argument (signature is `_makeMiddleware(fields, fileStrategy)` at index.js:26). Subclassing or monkey-patching Multer and overriding single/array/fields/none/any but forgetting to pass a strategy constant. Replacing `Multer.prototype._makeMiddleware` with a wrapper that drops the second arg. Constructing `new FileAppender(undefined, req)` directly from a custom storage plugin or fork. The strategy is set ONLY inside the setup() closures returned by the public methods, so any path that bypasses those closures and supplies its own setup() without a `fileStrategy` key reproduces the error.
Common situations: Developers extending Multer (e.g. to add a custom field handler) who call `this._makeMiddleware(myFields)` and omit the strategy. A fork or wrapper that copies index.js but misses one of the strategy constants. A monkey-patch (e.g. for logging) that wraps `_makeMiddleware` and forwards only the first argument. Loading two different multer versions where one monkey-patches the other. Almost never seen in vanilla usage of the documented API.
Related errors
AI-assisted analysis of expressjs/multer@ece6735083 (2026-08-05).
Data as JSON: /api/errors/af74b68b67037989.
Report an issue: GitHub.
Appendix: source
Thrown at lib/file-appender.js:15
function arrayRemove (arr, item) {
var idx = arr.indexOf(item)
if (~idx) arr.splice(idx, 1)
}
function FileAppender (strategy, req) {
this.strategy = strategy
this.req = req
switch (strategy) {
case 'NONE': break
case 'VALUE': break
case 'ARRAY': req.files = []; break
case 'OBJECT': req.files = Object.create(null); break
default: throw new Error('Unknown file strategy: ' + strategy)
}
}
FileAppender.prototype.insertPlaceholder = function (file) {
var placeholder = {
fieldname: file.fieldname
}
switch (this.strategy) {
case 'NONE': break
case 'VALUE': break
case 'ARRAY': this.req.files.push(placeholder); break
case 'OBJECT':
if (this.req.files[file.fieldname]) {
this.req.files[file.fieldname].push(placeholder)
} else {
this.req.files[file.fieldname] = [placeholder]
}View on GitHub (pinned to ece6735083)