rwf2/Rocket · error · io::Error

UnexpectedEof

UnexpectedEof

Error message

data limit exceeded

What it means

Runtime error produced by the impl_from_data_capped!-style macro in core/lib/src/data/capped.rs: a FromData guard (e.g. strict Form<T>) wraps the raw data in Capped<T>, and when the stream completes but the guard reports is_complete() == false — i.e. the configured data limit was hit before the body ended — the wrapper returns an io::Error of kind UnexpectedEof ('data limit exceeded') and the request fails with 400 BadRequest. The limit comes from Rocket's limits config (e.g. limits.forms, default 32 KiB).

Solutions

  1. Raise the limit in Rocket.toml: [default.limits] forms = "2 MiB" (or limits = { forms = "2 MiB" })
  2. Switch file-upload fields to TempFile<'_> or Data<'_> which stream to disk instead of parsing through the forms limit
  3. Use Capped<Form<T>> as the guard to accept oversized-but-parseable input and branch on is_complete()/n
  4. Register a 400/413 catcher to return a friendly message when a client still exceeds the limit

Example fix

# before
# Rocket.toml (defaults: forms = 32 KiB)
#[post("/submit", data = "<form>")]
fn submit(form: Form<Report>) { }

# after
# Rocket.toml
[default.limits]
forms = "2 MiB"
files = "10 MiB"

#[post("/submit", data = "<form>")]
fn submit(form: Form<Report>) { }
Defensive patterns

Strategy: try-catch

Validate before calling

// reject oversized form bodies before the guard parses them
#[post("/submit", data = "<data>")]
fn submit(data: Data<'_>) -> Status {
    let forms_limit = 2 * 1024 * 1024;
    match data.open(forms_limit.into()).into_string().await {
        Ok(s) if s.is_complete() => { /* parse manually or forward */ Status::Ok }
        Ok(_) => Status::PayloadTooLarge,
        Err(_) => Status::BadRequest,
    }
}

Try / catch

// accept-and-inspect oversized input instead of failing hard
#[post("/submit", data = "<form>")]
fn submit(form: Capped<Form<Report>>) -> Status {
    if form.is_complete() { Status::Ok } else { Status::PayloadTooLarge }
}

// and a catcher for other routes
#[catch(400)]
fn bad_request(_: &Request) -> &'static str { "payload too large or malformed" }

Prevention

When it happens

Trigger: POSTing a multipart/form or urlencoded body larger than limits.forms (default 32 KiB) to a route with a Form<T> guard; any capped guard whose limit the client exceeds. The boundary check is exact: hitting the cap without a terminating boundary yields an incomplete Capped value.

Common situations: File uploads routed through Form<T> instead of TempFile/Data; raising expectations in dev with small test payloads then receiving real-world sizes; forgetting Rocket's conservative 32 KiB default for forms after upgrading from an older version.

Related errors


AI-assisted analysis of rwf2/Rocket@3a54d079ae (2026-08-16). Data as JSON: /api/errors/2a67fffa18758fdd. Report an issue: GitHub.

Appendix: source

Thrown at core/lib/src/data/capped.rs:262

}

macro_rules! impl_strict_from_data_from_capped {
    ($T:ty) => (
        #[crate::async_trait]
        impl<'r> $crate::data::FromData<'r> for $T {
            type Error = <$crate::data::Capped<Self> as $crate::data::FromData<'r>>::Error;

            async fn from_data(
                r: &'r $crate::Request<'_>,
                d: $crate::Data<'r>
            ) -> $crate::data::Outcome<'r, Self> {
                use $crate::outcome::Outcome::*;
                use std::io::{Error, ErrorKind::UnexpectedEof};

                match <$crate::data::Capped<$T> as FromData>::from_data(r, d).await {
                    Success(p) if p.is_complete() => Success(p.into_inner()),
                    Success(_) => {
                        let e = Error::new(UnexpectedEof, "data limit exceeded");
                        Error((Status::BadRequest, e.into()))
                    },
                    Forward(d) => Forward(d),
                    Error((s, e)) => Error((s, e)),
                }
            }
        }
    )
}

View on GitHub (pinned to 3a54d079ae)