Your REST Assured multiPart() upload reaches the server with an empty file name. Why?
answer
- the content shape picks the body class
- text parts and binary parts differ here
- StringBody has no file-name slot
- convert the text to bytes instead
basics
~20 sText content carries no file name in REST Assured. String content becomes an Apache StringBody, which has no filename slot, so MultiPartConfig.defaultFileName() and MultiPartSpecBuilder.fileName() are both ignored. Only File, byte array and stream parts carry one.
solid answer
~50 sThe part was built from **`String` content**, and text parts have no file name at all. Internally REST Assured maps each part to an Apache HttpClient content body by the shape of what you passed: a `File` becomes a `FileBody`, and a `byte[]` or `InputStream` becomes an `InputStreamBody` — both of which take a file name. A `String`, or an object that serialized to one, becomes a `StringBody`, which has no file-name slot, so the value is dropped on the floor. `MultiPartSpecBuilder.fileName(...)` says as much in its own contract, and `MultiPartConfig.defaultFileName()` cannot rescue it either. The fix is to change the content shape, not the configuration: hand the same text over as bytes with `multiPart("weighInLog", "harris-weights.csv", csv.getBytes(UTF_8), "text/csv")`. `emptyDefaultFileName()` addresses the opposite case — clearing the name REST Assured otherwise supplies for binary content that arrived without one.
code
java · 13 linesimport static java.nio.charset.StandardCharsets.UTF_8;
import static io.restassured.RestAssured.given;
String csv = "hawkId,gramsBeforeFlight,weighedAt\nHW-118,642,2026-09-07T07:15Z\n";
// multiPart("weighInLog", csv) would send NO file name at all:
// String content becomes a StringBody, which has no filename slot.
given()
.multiPart("weighInLog", "harris-weights.csv", csv.getBytes(UTF_8), "text/csv")
.when()
.post("/hawks/HW-118/weigh-ins/imports")
.then()
.statusCode(202);go deeper
Know that multiPart() takes text as well as files, and that the two are not interchangeable. Recognising that a text field and a file upload are different part shapes is enough here.
Explain that the content's shape chooses the underlying body class, and that only the file, stream and byte-array shapes have somewhere to put a file name.
Diagnose this from the symptom without a debugger: read the overload that was called, identify the content shape, and propose the byte-array rewrite rather than a config change.
Set the team's line on when a part is a field and when it is a file, so upload helpers do not accumulate configuration tweaks that quietly do nothing.
## The symptom and the one-line cause You attach what you believe is a named CSV of hawk weights, the endpoint accepts the part, and the file name it reports back is empty. Nothing in the DSL warns you. The cause is not configuration and not the server: **the part's content was a `String`, and a text part carries no file name.** REST Assured stores each part as an internal record holding content, control name, file name, mime type, charset and headers. At send time it converts that record into an Apache HttpClient content body, choosing the class by the runtime shape of the content: - `File` content becomes a `FileBody`, constructed with the file name. - `InputStream` content becomes an `InputStreamBody`, constructed with the file name. - `byte[]` content is wrapped in a `ByteArrayInputStream` and also becomes an `InputStreamBody`, with the file name. - `String` content becomes a `StringBody`, which is constructed with **content and content type only**. - Anything else is converted with `toString()` and also becomes a `StringBody`. `StringBody` has no file-name parameter, so whatever file name the record held simply never reaches the wire. ## Which knobs are therefore dead on text content Three separate mechanisms look as though they should set a file name, and none of them works for text: | you write | effect on a String part | effect on a File, byte[] or stream part | | --- | --- | --- | | `MultiPartSpecBuilder.fileName("x")` | ignored | sets the file name | | `MultiPartConfig.defaultFileName("x")` | ignored | supplies the name when none was given | | `multiPart(name, "x", obj, mimeType)` | ignored once `obj` serializes to text | sets the file name | `MultiPartSpecBuilder.fileName(...)` states this in its own contract: it applies to input streams, byte arrays and files, and *not* to string content. That sentence is easy to skim past when you are reading the builder for its other methods. The mirror image is worth knowing too. `MultiPartConfig.defaultFileName()` ships as the literal string `file` and `emptyDefaultFileName()` clears it, but both only bite on binary content that arrived through a `MultiPartSpecification` without an explicit name — for example `new MultiPartSpecBuilder(csvBytes).controlName("weighInLog").build()`, which then goes out named `file`. That is the case where you *do* want `emptyDefaultFileName()`. ## The fix: change the shape, not the setting Since the class is chosen from the content's shape, give it a shape that carries a name: ```java String csv = "hawkId,gramsBeforeFlight,weighedAt\n" + "HW-118,642,2026-09-07T07:15Z\n"; given().multiPart("weighInLog", "harris-weights.csv", csv.getBytes(UTF_8), "text/csv") .when().post("/hawks/HW-118/weigh-ins/imports") .then().statusCode(202); ``` The overload that takes a control name, a file name, a `byte[]` and a mime type is the direct route, and its `InputStream` twin behaves identically. Three practical notes: 1. Encode explicitly. `getBytes(UTF_8)` beats `getBytes()`, whose result depends on the running JVM's default charset. 2. Do not reach for `MultiPartSpecBuilder.charset(...)` on the bytes — it rejects `byte[]` and `InputStream` content with an `IllegalArgumentException`, because there is no text left to encode. Put the charset in the mime type instead: `"text/csv; charset=UTF-8"`. 3. Keep the mime type honest. A part built from bytes defaults to `application/octet-stream`, and an endpoint that switches on the part type will reject that where it would accept `text/csv`. ## When a text part is the right answer anyway Not every part wants a file name. A plain form field — a falconer id, a fat score, a free-text note beside an uploaded photo — is exactly what a text part is for, and having no file name is what makes it read as a field rather than an upload. So the diagnosis has two possible endings: - The part really is a field. Leave it as `multiPart("falconerId", "F-4417")` and stop chasing the name. - The part really is a file. Convert the content to bytes or a stream and name it there. Choosing between them is the actual decision, and it belongs to the endpoint's contract rather than to REST Assured. ## What to check when you are diagnosing this - **Look at the argument type, not the variable name.** A serialized POJO is text by the time it becomes a part, so the object overloads land in the same place as raw strings. - **Check for a stray widening.** Passing something typed as `Object` picks the object overload and serializes it, which converts your intent into text without telling you. - **Read the endpoint's expectation.** A server that binds the part as a plain field will not care about a file name; one that binds it as an upload usually rejects an unnamed part outright. - **Prefer an explicit name over a configured default.** `multiPart(name, fileName, bytes, mimeType)` is readable at the call site; a default set three classes away is not.
- When does MultiPartConfig.defaultFileName() actually change what goes out?Only for `File`, `byte[]` or `InputStream` content that reached `multiPart(spec)` without an explicitly specified name — typically a part built by `MultiPartSpecBuilder` where you called neither `fileName(...)` nor `emptyFileName()`. Such a part goes out named `file`, the shipped default, and `emptyDefaultFileName()` clears it. On text content the setting has no effect at all.
saying these in an interview costs you the question
- Blames the server for stripping the file name off the part
- Thinks MultiPartConfig.defaultFileName() fixes a String part
- Says MultiPartSpecBuilder.fileName() applies to every content kind
- Suggests setting a charset on byte[] content to force a name
- Assumes every multipart part must carry a file name