OpenFeign/feign · error · HttpMessageNotReadableException
Multipart body could not be read.
Error message
Multipart body could not be read.
What it means
SpringManyMultipartFilesReader is a Spring HttpMessageConverter that parses a multipart/mixed request body into an array of ByteArrayMultipartFile. When reading any part of the multipart stream fails (malformed boundary, truncated body, I/O error), it wraps the cause in Spring's HttpMessageNotReadableException to signal the request body is unreadable.
Solutions
- Verify the client's Content-Type header is multipart/form-data with a boundary parameter matching the body's delimiters
- Compare the raw request body (e.g. via a logging filter) against the RFC 2046 multipart format the client produced
- Check for proxies/load balancers that truncate or re-encode request bodies
- If a custom encoder produced the body, ensure it writes proper CRLF-delimited boundary lines
Example fix
// before: client sends Content-Type: multipart/form-data (no boundary)
header("Content-Type", "multipart/form-data");
// after: let the form encoder set the boundary
header("Content-Type", "multipart/form-data; boundary=" + boundary); Defensive patterns
Strategy: try-catch
Validate before calling
String ct = request.getHeaders().getFirst("Content-Type");
if (ct == null || !ct.contains("boundary=")) {
throw new IllegalArgumentException("multipart Content-Type must carry a boundary");
} Try / catch
try {
MultipartFile[] parts = converter.read(type, context);
} catch (HttpMessageNotReadableException e) {
logger.warn("malformed multipart body", e.getCause());
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "malformed multipart body", e);
} Prevention
- Always send multipart bodies via an encoder that sets the boundary itself
- Log raw bodies in dev to catch malformed multipart payloads early
- Test against proxies/gateways that may alter or truncate bodies
When it happens
Trigger: Calling readInternal on a request whose multipart body is malformed: wrong or missing boundary parameter, truncated stream, or readMultiPart throwing (e.g. bad Content-Disposition headers or an IOException from MultipartStream).
Common situations: A Feign/proxy client posts multipart data to a Spring controller endpoint that decodes with this converter; the client encodes with a different boundary convention, a gateway truncates the body, or the Content-Type boundary was altered in transit.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- SpringManyMultipartFilesReader does not support writing to…
- Content-Type missing boundary information.
- Content-Disposition is not of type form-data.
- Unable to encode ( ) ...
- Getting multipart file's content bytes error
AI-assisted analysis of OpenFeign/feign@e2a1e27560 (2026-09-10).
Data as JSON: /api/errors/6f2df16e3cd2614f.
Report an issue: GitHub.
Appendix: source
Thrown at form-spring/src/main/java/feign/form/spring/converter/SpringManyMultipartFilesReader.java:109
MediaType contentType = headers.getContentType();
if (contentType == null) {
throw new HttpMessageNotReadableException("Content-Type is missing.", inputMessage);
}
val boundaryBytes = getMultiPartBoundary(contentType);
MultipartStream multipartStream =
new MultipartStream(inputMessage.getBody(), boundaryBytes, bufSize, null);
val multiparts = new LinkedList<ByteArrayMultipartFile>();
for (boolean nextPart = multipartStream.skipPreamble();
nextPart;
nextPart = multipartStream.readBoundary()) {
ByteArrayMultipartFile multiPart;
try {
multiPart = readMultiPart(multipartStream);
} catch (Exception e) {
throw new HttpMessageNotReadableException(
"Multipart body could not be read.", e, inputMessage);
}
multiparts.add(multiPart);
}
return multiparts.toArray(new ByteArrayMultipartFile[0]);
}
@Override
protected void writeInternal(
MultipartFile[] byteArrayMultipartFiles, HttpOutputMessage outputMessage) {
throw new UnsupportedOperationException(
getClass().getSimpleName() + " does not support writing to HTTP body.");
}
private byte[] getMultiPartBoundary(MediaType contentType) {
val boundaryString = unquote(contentType.getParameter("boundary"));
if (StringUtils.hasLength(boundaryString) == false) {
throw new HttpMessageConversionException("Content-Type missing boundary information.");View on GitHub (pinned to e2a1e27560)