Using Capped<TempFile> resets the network connection, Rocket doesn't notice.
Rocket Version
0.5.1
Operating System
CachyOS / Linux 7.1.4-1-cachyos
Rust Toolchain Version
rustc 1.96.1 (31fca3adb 2026-06-26) and rustc 1.99.0-nightly (f10db292a 2026-07-07)
What happened?
When using Capped<TempFile<'_>> as a function parameter, Rocket doesn't early-return if a limit is exceeded. Instead it just marks the Capped as incomplete (which you can check using .is_complete(), and lets you handle the too-large file the way you want.
This is great for my usecase because I need to handle a file upload and want to return different custom error messages if the file is e.g. too large.
However, if a limit is exceeded, Rocket doesn't send a Response. It calls the endpoint function, and it logs the Reponse as succesful, however it doesn't return data to the browser.
I tested this on Firefox, and Firefox shows an NS_ERROR_NET_RESET without a response body or response headers. Rocket however logs a status code and that the reponse succeeded.
During my testing, I did the following:
- Opened
localhost:8000 - Selected a 80 KiB JPEG file, got a 200 OK (in the network analyzer from Firefox developer tools)
- Selected a 40 MiB FLAC file, which is above the file size limit. Firefox showed a failed request (NS_ERROR_NET_RESET)
What's also interesting is that the request for the FLAC file was received twice by rocket. Maybe this is the browser automatically retrying or something, i don't know.
Test Case
use rocket::{data::Capped, fs::{NamedFile, TempFile}, get, http::Status, launch, post, routes};
#[post("/upload_file", data = "<file>")]
fn upload_file(
file: Capped<TempFile<'_>>
) -> (Status, &'static str) {
if file.is_complete() {
(Status::Ok, "Success!!!")
}
else {
(Status::PayloadTooLarge, "The file was too large.")
}
}
#[get("/")]
async fn index() -> NamedFile {
NamedFile::open("index.html").await.unwrap()
}
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/", routes![index, upload_file])
}Log Output
-- configuration trace information --
>> "address" parameter source: rocket::Config::default()
>> "port" parameter source: rocket::Config::default()
>> "workers" parameter source: rocket::Config::default()
>> "max_blocking" parameter source: rocket::Config::default()
>> "keep_alive" parameter source: rocket::Config::default()
>> "ident" parameter source: rocket::Config::default()
>> "ip_header" parameter source: rocket::Config::default()
>> "limits" parameter source: rocket::Config::default()
>> "temp_dir" parameter source: rocket::Config::default()
>> "log_level" parameter source: `ROCKET_` environment variable(s)
>> "shutdown" parameter source: rocket::Config::default()
>> "cli_colors" parameter source: rocket::Config::default()
Configured for debug.
>> address: 127.0.0.1
>> port: 8000
>> workers: 12
>> max blocking threads: 512
>> ident: Rocket
>> IP header: X-Real-IP
>> limits: bytes = 8KiB, data-form = 2MiB, file = 1MiB, form = 32KiB, json = 1MiB, msgpack = 1MiB, string = 8KiB
>> temp dir: /tmp
>> http/2: true
>> keep-alive: 5s
>> tls: disabled
>> shutdown: ctrlc = true, force = true, signals = [SIGTERM], grace = 2s, mercy = 3s
>> log level: debug
>> cli colors: true
Routes:
>> (index) GET /
>> (upload_file) POST /upload_file
Fairings:
>> Shield (liftoff, response, singleton)
Shield:
>> X-Frame-Options: SAMEORIGIN
>> Permissions-Policy: interest-cohort=()
>> X-Content-Type-Options: nosniff
Rocket has launched from http://127.0.0.1:8000
--> /home/tom/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rocket-0.5.1/src/server.rs:77
received request: Request {
method: GET,
uri: /,
version: HTTP/1.1,
headers: {
"host": "localhost:8000",
"user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:153.0) Gecko/20100101 Firefox/153.0",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"accept-language": "de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7",
"accept-encoding": "gzip, deflate, br, zstd",
"sec-gpc": "1",
"connection": "keep-alive",
"cookie": "team_id=5SUxbtcAMU3kN02W0S2SCwyQ0Lya0wwzoXmJPL4%3D",
"upgrade-insecure-requests": "1",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"priority": "u=0, i",
},
body: Body(
Empty,
),
}
GET / text/html:
>> Matched: (index) GET /
>> Outcome: Success(200 OK)
--> /home/tom/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rocket-0.5.1/src/server.rs:166
sending response: Response {
status: 200,
version: HTTP/1.1,
headers: {
"content-type": "text/html; charset=utf-8",
"server": "Rocket",
"x-frame-options": "SAMEORIGIN",
"permissions-policy": "interest-cohort=()",
"x-content-type-options": "nosniff",
"content-length": "526",
},
body: Body(
Streaming,
),
}
>> Response succeeded.
--> /home/tom/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rocket-0.5.1/src/server.rs:77
received request: Request {
method: POST,
uri: /upload_file,
version: HTTP/1.1,
headers: {
"host": "localhost:8000",
"user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:153.0) Gecko/20100101 Firefox/153.0",
"accept": "*/*",
"accept-language": "de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7",
"accept-encoding": "gzip, deflate, br, zstd",
"referer": "http://localhost:8000/",
"content-type": "image/jpeg",
"content-length": "78081",
"origin": "http://localhost:8000",
"sec-gpc": "1",
"connection": "keep-alive",
"cookie": "team_id=5SUxbtcAMU3kN02W0S2SCwyQ0Lya0wwzoXmJPL4%3D",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"priority": "u=0",
},
body: Body(
Streaming,
),
}
POST /upload_file image/jpeg:
>> Matched: (upload_file) POST /upload_file
>> Outcome: Success(200 OK)
--> /home/tom/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rocket-0.5.1/src/server.rs:166
sending response: Response {
status: 200,
version: HTTP/1.1,
headers: {
"content-type": "text/plain; charset=utf-8",
"server": "Rocket",
"x-frame-options": "SAMEORIGIN",
"permissions-policy": "interest-cohort=()",
"x-content-type-options": "nosniff",
"content-length": "10",
},
body: Body(
Streaming,
),
}
>> Response succeeded.
--> /home/tom/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rocket-0.5.1/src/server.rs:77
received request: Request {
method: POST,
uri: /upload_file,
version: HTTP/1.1,
headers: {
"host": "localhost:8000",
"user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:153.0) Gecko/20100101 Firefox/153.0",
"accept": "*/*",
"accept-language": "de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7",
"accept-encoding": "gzip, deflate, br, zstd",
"referer": "http://localhost:8000/",
"content-type": "audio/flac",
"content-length": "40204828",
"origin": "http://localhost:8000",
"sec-gpc": "1",
"connection": "keep-alive",
"cookie": "team_id=5SUxbtcAMU3kN02W0S2SCwyQ0Lya0wwzoXmJPL4%3D",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"priority": "u=0",
},
body: Body(
Streaming,
),
}
POST /upload_file audio/flac:
>> Matched: (upload_file) POST /upload_file
>> Data limit reached while reading the request body.
>> Outcome: Success(413 Payload Too Large)
--> /home/tom/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rocket-0.5.1/src/server.rs:166
sending response: Response {
status: 413,
version: HTTP/1.1,
headers: {
"content-type": "text/plain; charset=utf-8",
"server": "Rocket",
"x-frame-options": "SAMEORIGIN",
"permissions-policy": "interest-cohort=()",
"x-content-type-options": "nosniff",
"content-length": "23",
},
body: Body(
Streaming,
),
}
>> Response succeeded.
--> /home/tom/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rocket-0.5.1/src/server.rs:77
received request: Request {
method: POST,
uri: /upload_file,
version: HTTP/1.1,
headers: {
"host": "localhost:8000",
"user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:153.0) Gecko/20100101 Firefox/153.0",
"accept": "*/*",
"accept-language": "de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7",
"accept-encoding": "gzip, deflate, br, zstd",
"referer": "http://localhost:8000/",
"content-type": "audio/flac",
"content-length": "40204828",
"origin": "http://localhost:8000",
"sec-gpc": "1",
"connection": "keep-alive",
"cookie": "team_id=5SUxbtcAMU3kN02W0S2SCwyQ0Lya0wwzoXmJPL4%3D",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"priority": "u=0",
},
body: Body(
Streaming,
),
}
POST /upload_file audio/flac:
>> Matched: (upload_file) POST /upload_file
>> Data limit reached while reading the request body.
>> Outcome: Success(413 Payload Too Large)
--> /home/tom/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rocket-0.5.1/src/server.rs:166
sending response: Response {
status: 413,
version: HTTP/1.1,
headers: {
"content-type": "text/plain; charset=utf-8",
"server": "Rocket",
"x-frame-options": "SAMEORIGIN",
"permissions-policy": "interest-cohort=()",
"x-content-type-options": "nosniff",
"content-length": "23",
},
body: Body(
Streaming,
),
}
>> Response succeeded.Additional Context
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="file" id="fileinput">
<button id="button">Upload</button>
</body>
<script>
document.getElementById("button").addEventListener("click", () => {
const file = document.getElementById("fileinput").files[0];
fetch("/upload_file", {
method: "POST",
body: file
}).then(resp => {
console.log(resp)
})
})
</script>
</html>System Checks
- My bug report relates to functionality.
- I have tested against the latest Rocket release or a recent git commit.
- I have tested against the latest stable
rustctoolchain. - I was unable to find this issue previously reported.
Source: rwf2/Rocket