Skip to content

Commit 28c5620

Browse files
prakticodeclaudemicalevisk
authored
fix(express): map multer errors by code instead of message (#17769)
* fix(express): map multer errors by code instead of message multer 2.4.0 changed the LIMIT_UNEXPECTED_FILE message from "Unexpected field" to "Unexpected file field". transformException compared error.message against the old text, so a file sent under an unexpected field name became a 500 instead of a 400. Map multer errors by their code, which multer documents as the stable identifier, and keep matching on the message for errors without one, such as busboy's. The LIMIT_UNEXPECTED_FILE constant is updated to the new message. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Update packages/platform-express/multer/multer/multer.utils.ts Co-authored-by: Micael Levi L. Cavalcante <micalevisk@gmail.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Micael Levi L. Cavalcante <micalevisk@gmail.com>
1 parent d59ad3a commit 28c5620

3 files changed

Lines changed: 45 additions & 3 deletions

File tree

packages/platform-express/multer/multer/multer.constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const multerExceptions = {
66
LIMIT_FIELD_KEY: 'Field name too long',
77
LIMIT_FIELD_VALUE: 'Field value too long',
88
LIMIT_FIELD_COUNT: 'Too many fields',
9-
LIMIT_UNEXPECTED_FILE: 'Unexpected field',
9+
LIMIT_UNEXPECTED_FILE: 'Unexpected file field',
1010
MISSING_FIELD_NAME: 'Field name missing',
1111
LIMIT_FIELD_NESTING: 'Field name nesting too deep',
1212
};

packages/platform-express/multer/multer/multer.utils.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@ import { multerExceptions, busboyExceptions } from './multer.constants.js';
88
// Multer may add in a 'field' property to the error
99
// https://github.com/expressjs/multer/blob/aa42bea6ac7d0cb8fcb279b15a7278cda805dc63/lib/multer-error.js#L19
1010
export function transformException(
11-
error: (Error & { field?: string }) | undefined,
11+
error: (Error & { field?: string; code?: string }) | undefined,
1212
) {
1313
if (!error || error instanceof HttpException) {
1414
return error;
1515
}
16-
switch (error.message) {
16+
// Multer identifies its errors by `code`, while the messages may change
17+
// between releases (e.g. "Unexpected field" became "Unexpected file field")
18+
const exception = isMulterExceptionCode(error.code)
19+
? multerExceptions[error.code]
20+
: error.message;
21+
switch (exception) {
1722
case multerExceptions.LIMIT_FILE_SIZE:
1823
return new PayloadTooLargeException(error.message);
1924
case multerExceptions.LIMIT_FILE_COUNT:
@@ -37,3 +42,9 @@ export function transformException(
3742
}
3843
return error;
3944
}
45+
46+
function isMulterExceptionCode(
47+
code: unknown,
48+
): code is keyof typeof multerExceptions {
49+
return typeof code === 'string' && Object.hasOwn(multerExceptions, code);
50+
}

packages/platform-express/test/multer/multer/multer.utils.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
HttpException,
44
PayloadTooLargeException,
55
} from '@nestjs/common';
6+
import multer from 'multer';
67
import {
78
busboyExceptions,
89
multerExceptions,
@@ -71,6 +72,36 @@ describe('transformException', () => {
7172
);
7273
});
7374
});
75+
describe('and is an error thrown by multer', () => {
76+
it('should return "BadRequestException" for LIMIT_UNEXPECTED_FILE', () => {
77+
const err = new multer.MulterError('LIMIT_UNEXPECTED_FILE', 'photo');
78+
const result = transformException(err);
79+
expect(result).toBeInstanceOf(BadRequestException);
80+
expect(result!.message).toBe(`${err.message} - photo`);
81+
});
82+
it('should return "PayloadTooLargeException" for LIMIT_FILE_SIZE', () => {
83+
const err = new multer.MulterError('LIMIT_FILE_SIZE', 'avatar');
84+
expect(transformException(err)).toBeInstanceOf(
85+
PayloadTooLargeException,
86+
);
87+
});
88+
it('should map by code even when the message differs', () => {
89+
const err = {
90+
code: 'LIMIT_UNEXPECTED_FILE',
91+
message: 'Some other wording',
92+
field: 'photo',
93+
};
94+
const result = transformException(err as any);
95+
expect(result).toBeInstanceOf(BadRequestException);
96+
expect(result!.message).toBe('Some other wording - photo');
97+
});
98+
it('should behave as identity for a code that is not a multer code', () => {
99+
const err = Object.assign(new Error('no such file'), {
100+
code: 'ENOENT',
101+
});
102+
expect(transformException(err)).toBe(err);
103+
});
104+
});
74105
describe(`and has a 'field' property`, () => {
75106
it('should return the field propery appended to the error message', () => {
76107
const err = {

0 commit comments

Comments
 (0)