rustfs/rustfs · error · std::io::Error

Last-Modified time format is invalid

Error message

Last-Modified time format is invalid

What it means

While statting a remote-tier object, the Last-Modified header could not be parsed as either an RFC 2822 HTTP-date or an RFC 3339 timestamp, so the whole stat conversion fails with InvalidData. S3 always returns Last-Modified in RFC 2822 form (e.g., 'Sun, 16 Aug 2026 12:00:00 GMT'); anything else marks a non-compliant responder.

Source

Thrown at crates/ecstore/src/client/transition_api.rs:1265

        let content_length_str = get_header("Content-Length");
        if !content_length_str.is_empty() {
            content_length_str
                .parse::<i64>()
                .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "Content-Length is not an integer"))?
        } else {
            -1
        }
    };

    // Parse Last-Modified time
    let mod_time = {
        let last_modified_str = get_header("Last-Modified");
        if !last_modified_str.is_empty() {
            // Parse HTTP date format (RFC 7231)
            // Using time crate to parse HTTP dates
            let parsed_time = OffsetDateTime::parse(&last_modified_str, &time::format_description::well_known::Rfc2822)
                .or_else(|_| OffsetDateTime::parse(&last_modified_str, &time::format_description::well_known::Rfc3339))
                .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "Last-Modified time format is invalid"))?;
            Some(parsed_time)
        } else {
            Some(OffsetDateTime::now_utc())
        }
    };

    // Get content type
    let content_type = {
        let content_type_raw = get_header("Content-Type");
        let content_type_trimmed = content_type_raw.trim();
        if content_type_trimmed.is_empty() {
            Some("application/octet-stream".to_string())
        } else {
            Some(content_type_trimmed.to_string())
        }
    };

    // Parse Expires time

View on GitHub (pinned to 9e6e02ea09)

Solutions

  1. Inspect the raw Last-Modified value returned by the endpoint with curl -I.
  2. Fix the endpoint or proxy to emit a proper RFC 2822 HTTP-date.
  3. For emulators you control, format dates with a standard HTTP-date formatter instead of locale-dependent to_string output.
Defensive patterns

Strategy: try-catch

Try / catch

match stat_object_from_headers(&headers) {
    Ok(info) => Ok(info),
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {
        tracing::error!(event = "tier_stat_header_invalid", header = "Last-Modified", "unparseable HTTP-date from tier; failing closed");
        Err(e)
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Object stat on a tier endpoint or proxy that emits a nonstandard Last-Modified - epoch seconds, locale-formatted dates, or a header overwritten by an intermediary.

Common situations: CDNs/reverse proxies rewriting dates in transit; homegrown S3 emulators in CI using ISO strings with offsets or locale formats; gateways normalizing dates to their own format.

Related errors


AI-assisted analysis of rustfs/rustfs@9e6e02ea09 (2026-08-16). Data as JSON: /api/errors/2e89a09b11d4801f. Report an issue: GitHub.