Skip to content

vacuum fails on S3: "Got invalid DeleteObjects response: unknown variant Code" — fixed by object_store 0.14 #4644

Description

@sergey-suslov

Environment

  • deltalake 0.32.4 (latest release), deltalake-aws 0.15.0
  • object_store 0.13.2 (pinned transitively)
  • AWS S3

What happens

VacuumBuilder fails partway through a large vacuum:

Delta table error: Failed to read delta log object: Generic S3 error:
Got invalid DeleteObjects response: unknown variant `Code`, expected `Deleted` or `Error`

(The Failed to read delta log object prefix is just the text on DeltaTableError::ObjectStore; nothing is being read — this is the delete path.)

The whole maintenance run aborts, so nothing is reclaimed.

Root cause

Two changes compose into this:

1. object_store 0.13 routes all S3 deletes through the bulk API.

0.13 added a delete_stream override to PrefixStore that forwards to the inner store (src/prefix.rs). In 0.12.x that override did not exist, so PrefixStore used the trait's default implementation — individual DELETEs at .buffered(10).

delta-rs vacuum deletes via the prefix-wrapped store:

// deltalake-core/src/operations/vacuum.rs:517
let files_deleted = store
    .object_store(Some(operation_id))
    .delete_stream(locations)

That now reaches AmazonS3::delete_stream.try_chunks(1_000)bulk_delete_requestPOST /?delete.

(0.13 also removed delete from the ObjectStore trait — ObjectStoreExt::delete funnels a single path through delete_stream — so single-object deletes take the same route.)

2. bulk_delete_request in 0.13.2 has no guard for S3's 200-with-error-body.

send_retry returns Ok only for 2xx, so this is not a 4xx/5xx: S3 answered HTTP 200 with a body whose first child element is <Code>, i.e. an <Error> document rather than a <DeleteResult>. object_store handles exactly this case via body_contains_error() (InternalError / SlowDown), but only when retry_error_body is set — and in 0.13.2 bulk_delete_request is the one write path that doesn't set it (copy_request and create_multipart both do).

Given this fired ~15 minutes into a delete-heavy run doing up to 20 concurrent × 1000-key batches, S3 throttling (SlowDown) is the likely body — though I can't confirm the exact code, since the parse error doesn't carry it.

This is already fixed upstream

Both in object_store 0.14.0 (2026-06-18):

The ask

Bump object_store to 0.14 in the workspace. main currently pins:

object_store = { version = "0.13.2" }

There is no 0.13.x backport (0.13.2 → 0.14.0 directly), and ^0.13.2 excludes 0.14, so downstream users cannot pick the fix up by overriding the dependency. Nor can it be worked around downstream:

  • retry_error_body is pub(crate), so it isn't reachable from a custom store.
  • A custom ObjectStoreFactory wrapper that overrides delete_stream has nothing per-object to delegate to, since 0.13 removed delete from the trait — ObjectStoreExt::delete just calls delete_stream again.

So today the only options are pinning back to a delta-rs release on object_store 0.12, or carrying a patched fork.

Bumping would additionally expose aws_disable_bulk_delete through storage_options, giving users on S3-compatible backends a supported way to avoid DeleteObjects entirely.

I realise 0.14.0 is not a drop-in — it carries at least refactor!: remove cloud feature alias (#751, and deltalake-core enables object_store/cloud), Add Extensionsin*Result objects (#743), Make reqwest optional (#724) and Pluggable Crypto / Update reqwest 0.13 (#707). So this is a migration rather than a version-string change, and the reqwest 0.13 move in particular is a call for maintainers rather than a drive-by.

Happy to do the work if the bump is wanted and you can say how you'd like reqwest handled. Failing that, would a 0.13.3 backport of #733 alone be considered? It is a three-line change to bulk_delete_request and applies cleanly to the v0.13.2 tag, which would unblock every delta-rs user on the current release line without any of the 0.14 churn.

Metadata

Metadata

Assignees

No one assigned

    Labels

    binding/rustIssues for the Rust cratedependenciesPull requests that update a dependency filefix-awaiting-releaseIssues which have a fixed merged or pendingon-holdIssues and Pull Requests that are on hold for some reason

    Type

    No type

    Projects

    Status
    Done
    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions