neondatabase/neon · error

File size {request_len} exceeds max {max_len}

Error message

File size {request_len} exceeds max {max_len}

What it means

endpoint_storage's PUT handler buffers the whole request body in memory and rejects uploads larger than the configured max_upload_file_limit, returning a 400 with this message. The limit exists because remote_storage has no multipart support, so the service must hold the entire object in memory; the default is 100 MiB (100 * 1024 * 1024), configurable via the max_upload_file_limit field in the service's JSON config.

Source

Thrown at endpoint_storage/src/app.rs:93

        .status(StatusCode::OK)
        .header(CONTENT_TYPE, APPLICATION_OCTET_STREAM)
        .body(Body::from_stream(stream))
        .map_err(|e| internal_error(e, path, "reading response"))
}

// Best solution for files is multipart upload, but remote_storage doesn't support it,
// so we can either read Bytes in memory and push at once or forward BodyDataStream to
// remote_storage. The latter may seem more peformant, but BodyDataStream doesn't have a
// guaranteed size() which may produce issues while uploading to s3.
// So, currently we're going with an in-memory copy plus a boundary to prevent uploading
// very large files.
async fn set(S3Path { path }: S3Path, state: State, bytes: Bytes) -> Result {
    info!(%path, "uploading");
    let request_len = bytes.len();
    let max_len = state.max_upload_file_limit;
    if request_len > max_len {
        return Err(bad_request(
            anyhow!("File size {request_len} exceeds max {max_len}"),
            "uploading",
        ));
    }

    let cancel = state.cancel.clone();
    let fun = async || {
        let stream = bytes_to_stream(bytes.clone());
        state
            .storage
            .upload(stream, request_len, &path, None, &cancel)
            .await
    };
    retry(
        fun,
        TimeoutOrCancel::caused_by_cancel,
        WARN_THRESHOLD,
        MAX_RETRIES,
        "uploading",

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Reduce the upload size below the service's limit
  2. Raise max_upload_file_limit in the endpoint_storage JSON config and restart the service (mind the memory footprint — bodies are held in memory)
  3. Split or compress the artifact so it fits under the configured bound

Example fix

// endpoint_storage config (main.rs reads JSON config)
// before
{ "pemfile": "/pem", "listen": "0.0.0.0:51243", "storage_kind": {"S3": {...}} }
// after
{ "pemfile": "/pem", "listen": "0.0.0.0:51243", "max_upload_file_limit": 524288000, "storage_kind": {"S3": {...}} }
Defensive patterns

Strategy: validation

Validate before calling

// Client-side check before PUT: know the service's limit and your body size
let body_len = bytes.len();
if body_len > MAX_UPLOAD_FILE_LIMIT {
    return Err(anyhow::anyhow!(
        "body is {body_len} bytes, service limit is {MAX_UPLOAD_FILE_LIMIT}; split or raise max_upload_file_limit"
    ));
}
// or, when the limit is discoverable, compare Content-Length against it before sending

Try / catch

match resp.status() {
    StatusCode::BAD_REQUEST if body_contains(&resp, "exceeds max").await => {
        // permanent: do not retry; shrink payload or raise the server's max_upload_file_limit
        return Err(UploadTooLarge.into());
    }
    s if s.is_server_error() => { /* retryable */ }
    _ => {}
}

Prevention

When it happens

Trigger: PUT-ing a body whose length exceeds state.max_upload_file_limit to the endpoint_storage HTTP endpoint (e.g. an extension artifact upload). The size check runs before any storage call and returns bad_request.

Common situations: Uploading large custom extensions or binaries above 100 MiB to a deployment running the default limit; a config that lowered the limit for memory protection; clients that do not check Content-Length against the service's limit before sending.

Related errors


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/5c9ff804bff894cc. Report an issue: GitHub.