neondatabase/neon · error · DownloadError

Received truncated ListObjectVersions response for prefix={p

Error message

Received truncated ListObjectVersions response for prefix={prefix:?}

What it means

While paginating ListObjectVersions, the loop stops when a response carries no NextVersionIdMarker; if that response still reports IsTruncated=true, the S3 pagination contract is broken and this error aborts the listing. Real S3 always emits continuation markers on truncated responses, so this points at a non-compliant S3-compatible endpoint.

Source

Thrown at libs/remote_storage/src/s3_bucket.rs:511

                        key,
                        last_modified,
                        kind: crate::VersionKind::DeletionMarker,
                    })
                });
            itertools::process_results(versions.chain(deletes), |n_vds| {
                versions_and_deletes.extend(n_vds)
            })
            .map_err(DownloadError::Other)?;
            fn none_if_empty(v: Option<String>) -> Option<String> {
                v.filter(|v| !v.is_empty())
            }
            version_id_marker = none_if_empty(response.next_version_id_marker);
            key_marker = none_if_empty(response.next_key_marker);
            if version_id_marker.is_none() {
                // The final response is not supposed to be truncated
                if response.is_truncated.unwrap_or_default() {
                    return Err(DownloadError::Other(anyhow::anyhow!(
                        "Received truncated ListObjectVersions response for prefix={prefix:?}"
                    )));
                }
                break;
            }
            if let Some(max_keys) = max_keys {
                if versions_and_deletes.len() >= max_keys.get().try_into().unwrap() {
                    return Err(DownloadError::Other(anyhow::anyhow!("too many versions")));
                }
            }
        }
        Ok(VersionListing {
            versions: versions_and_deletes,
        })
    }

    pub fn bucket_name(&self) -> &str {
        &self.bucket_name
    }

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Reproduce against real S3 or MinIO to confirm it is an endpoint bug
  2. Upgrade the emulator/gateway to a version with correct ListObjectVersions pagination
  3. File an issue with the endpoint vendor, including the prefix and page size used
  4. As a workaround, narrow the prefix so the listing fits in fewer pages

Example fix

# before: CI uses an emulator with broken pagination
NEON_REMOTE_STORAGE_S3_URL=http://emulator:5000/bucket
# after
NEON_REMOTE_STORAGE_S3_URL=http://minio:9000/bucket
Defensive patterns

Strategy: try-catch

Validate before calling

use aws_sdk_s3::Client;

async fn pagination_is_compliant(client: &Client, bucket: &str) -> anyhow::Result<bool> {
    let resp = client.list_object_versions().bucket(bucket).max_keys(1).send().await?;
    let truncated = resp.is_truncated.unwrap_or(false);
    let has_marker = resp.next_version_id_marker.as_deref().map(|m| !m.is_empty()).unwrap_or(false);
    Ok(!truncated || has_marker)
}

Type guard

fn is_truncated_listing_violation(err: &remote_storage::DownloadError) -> bool {
    matches!(err, remote_storage::DownloadError::Other(e)
        if e.to_string().contains("truncated ListObjectVersions"))
}

Try / catch

if let Err(DownloadError::Other(e)) = storage.list_versions(&prefix, mode, None, &cancel).await {
    if e.to_string().contains("truncated ListObjectVersions") {
        // protocol violation: fail fast and flag the endpoint, do not retry
    }
}

Prevention

When it happens

Trigger: Calling list_versions or time_travel_recover against an emulator or gateway that truncates ListObjectVersions responses without setting next_version_id_marker/next_key_marker (or returns them empty).

Common situations: Testing against localstack-like emulators with partial ListObjectVersions support; S3 gateways that drop pagination params like max-keys or version-id-marker.

Related errors


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