From c42768e3a023631dfaaba10dd5f6f6b2377378fa Mon Sep 17 00:00:00 2001 From: Yarchik Date: Thu, 2 Jul 2026 04:37:08 +0100 Subject: [PATCH] fix: guard removePlaceholder against a missing field entry With upload.fields(), if a fileFilter invokes its callback more than once asynchronously, removePlaceholder runs twice for the same field: the first call deletes req.files[fieldname], the second reads .length of undefined and throws an uncaught TypeError that crashes the process. Break out when the field entry is already gone, matching the array strategy. Fixes #950. --- lib/file-appender.js | 1 + test/file-filter.js | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/lib/file-appender.js b/lib/file-appender.js index a760d8bb..8bc07ad6 100644 --- a/lib/file-appender.js +++ b/lib/file-appender.js @@ -43,6 +43,7 @@ FileAppender.prototype.removePlaceholder = function (placeholder) { case 'VALUE': break case 'ARRAY': arrayRemove(this.req.files, placeholder); break case 'OBJECT': + if (!this.req.files[placeholder.fieldname]) break if (this.req.files[placeholder.fieldname].length === 1) { delete this.req.files[placeholder.fieldname] } else { diff --git a/test/file-filter.js b/test/file-filter.js index 1d857f44..7783175f 100644 --- a/test/file-filter.js +++ b/test/file-filter.js @@ -53,4 +53,27 @@ describe('File Filter', function () { done() }) }) + + it('should not crash when fileFilter invokes the callback more than once', function (done) { + function rejectThenError (req, file, cb) { + setImmediate(function () { + cb(null, false) + cb(new Error('Fake error')) + }) + } + + var form = new FormData() + var upload = withFilter(rejectThenError) + var parser = upload.fields([ + { name: 'logo', maxCount: 1 }, + { name: 'banner', maxCount: 1 } + ]) + + form.append('logo', util.file('tiny0.dat')) + + util.submitForm(parser, form, function (err, req) { + assert.ok(err) + done() + }) + }) })