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
- Reproduce against real S3 or MinIO to confirm it is an endpoint bug
- Upgrade the emulator/gateway to a version with correct ListObjectVersions pagination
- File an issue with the endpoint vendor, including the prefix and page size used
- 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
- Run a pagination compliance probe before onboarding a new S3-compatible endpoint
- Pin emulator versions known to implement continuation markers correctly
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
- Missing ETag header
- Missing LastModified header
- Failed to delete {}/{} objects
- too many versions
- head_object doesn't contain last_modified or content_length
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/905bf3d4a65755d4.
Report an issue: GitHub.