From 47fef02bad39d710bfd6fba4085c18162e8af7d0 Mon Sep 17 00:00:00 2001 From: Andrea Bozzo Date: Thu, 26 Feb 2026 11:14:20 +0100 Subject: [PATCH] fix: Return specific error message when multipart body limit is exceeded (#3611) --- axum-extra/src/extract/multipart.rs | 23 ++++++++++++++++++++++- axum/src/extract/multipart.rs | 23 ++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index d74c0bc3..1163afce 100644 --- a/axum-extra/src/extract/multipart.rs +++ b/axum-extra/src/extract/multipart.rs @@ -247,7 +247,11 @@ impl MultipartError { /// Get the response body text used for this rejection. pub fn body_text(&self) -> String { - let body = self.source.to_string(); + let body = if is_body_limit_error(&self.source) { + "Request payload is too large".to_owned() + } else { + self.source.to_string() + }; axum_core::__log_rejection!( rejection_type = Self, body_text = body, @@ -298,6 +302,22 @@ fn status_code_from_multer_error(err: &multer::Error) -> StatusCode { } } +fn is_body_limit_error(err: &multer::Error) -> bool { + match err { + multer::Error::FieldSizeExceeded { .. } | multer::Error::StreamSizeExceeded { .. } => true, + multer::Error::StreamReadFailed(err) => { + if let Some(err) = err.downcast_ref::() { + return is_body_limit_error(err); + } + err.downcast_ref::() + .and_then(|err| err.source()) + .and_then(|err| err.downcast_ref::()) + .is_some() + } + _ => false, + } +} + impl IntoResponse for MultipartError { fn into_response(self) -> Response { (self.status(), self.body_text()).into_response() @@ -403,6 +423,7 @@ mod tests { let res = client.post("/").multipart(form).await; assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE); + assert_eq!(res.text().await, "Request payload is too large"); } #[tokio::test] diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 086033e0..081de75d 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -244,7 +244,11 @@ impl MultipartError { /// Get the response body text used for this rejection. #[must_use] pub fn body_text(&self) -> String { - self.source.to_string() + if is_body_limit_error(&self.source) { + "Request payload is too large".to_owned() + } else { + self.source.to_string() + } } /// Get the status code used for this rejection. @@ -289,6 +293,22 @@ fn status_code_from_multer_error(err: &multer::Error) -> StatusCode { } } +fn is_body_limit_error(err: &multer::Error) -> bool { + match err { + multer::Error::FieldSizeExceeded { .. } | multer::Error::StreamSizeExceeded { .. } => true, + multer::Error::StreamReadFailed(err) => { + if let Some(err) = err.downcast_ref::() { + return is_body_limit_error(err); + } + err.downcast_ref::() + .and_then(|err| err.source()) + .and_then(|err| err.downcast_ref::()) + .is_some() + } + _ => false, + } +} + impl fmt::Display for MultipartError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Error parsing `multipart/form-data` request") @@ -407,6 +427,7 @@ mod tests { let res = client.post("/").multipart(form).await; assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE); + assert_eq!(res.text().await, "Request payload is too large"); } #[crate::test]