Analyses / Object storage
Object ETags: checksums and conditional writes are different
12 October 2026 / Primary-source synthesis and local illustration
An ETag can select the object representation a client intends to update without being the checksum of the complete file. Conversely, a valid checksum does not stop a stale client from overwriting someone else's change. Treat validation of bytes and validation of a write's precondition as separate decisions.
Three questions, three different guarantees
| Question | Mechanism | Does not establish |
|---|---|---|
| Did these bytes match the expected bytes? | Checksum with a known algorithm, scope and trusted expected value | Which writer should win |
| Does this write still meet its condition? | Server-evaluated precondition on the target object | Application-level correctness or a cross-key transaction |
| What does a later read observe? | The service's consistency contract | An atomic GET-compute-PUT sequence |
ETag means validator, not a universal hash formula
HTTP defines an entity tag as opaque. A strong validator is not a promise of cryptographic strength, and a weak tag prefixed with W/ is not eligible for strong If-Match comparison. [1]
For S3, documented single-request PUT/POST/Copy cases using plaintext or SSE-S3 can have full-object MD5 ETags. SSE-KMS and SSE-C do not. Multipart Upload and Part Copy ETags are not the MD5 digest of the complete object, regardless of encryption. A hash-looking string alone is insufficient evidence. [2]
Dedicated checksum fields also require interpretation: FULL_OBJECT and COMPOSITE identify different coverage. A composite checksum is built from part checksums; it is not generally the result of hashing the downloaded file once. Record the type as well as the algorithm. [3]
Identical object bytes, different multipart values
The following local calculation follows the MD5 construction shown in AWS's multipart tutorial: hash each part, concatenate the raw digest bytes, hash that sequence and append the part count. Hashing hexadecimal text instead produces a different calculation. This is a documented example, not a universal ETag-generation rule. [4]
Synthetic input: Hesela storage (14 ASCII bytes). The tiny parts in the two-part examples are below S3 multipart minimum sizes. No network request or storage benchmark was run.
| Partition | Lengths | Whole-object MD5 | Tutorial-style multipart ETag |
|---|---|---|---|
| Split A: two parts | 7 + 7 | ac5980a8214ea2cf76e494a7f638adc6 | 51ba0db1c95870901e38295a3437765e-2 |
| Split B: two parts | 4 + 10 | ac5980a8214ea2cf76e494a7f638adc6 | 6df788a11cdd1908cabf429662c9651c-2 |
| One multipart part | 14 | ac5980a8214ea2cf76e494a7f638adc6 | 58e5a2b5fb3b3cbf3446cd8c0cf43ad5-1 |
The unchanged whole-object digest and changed multipart values isolate a boundary effect, not corruption. A one-part multipart construction also differs from a single PUT's full-object MD5. The part count alone cannot reconstruct where the boundaries were.
Strong consistency still permits a lost update
S3 documents strong read-after-write consistency and atomic updates to an individual key, but concurrent unconditional writes use last-writer-wins semantics. There is no built-in atomic update across keys. Two clients can read the same state, compute different edits and then overwrite each other, even when each individual operation is strongly consistent. [5]
For PutObject, If-Match requests a write only when the current ETag matches the supplied tag; If-None-Match: * requests creation only when there is no current object. A failed condition yields 412; a concurrent conflicting operation can yield 409. [6]
Engineering consequence: after a conflict, re-read and reconcile or reject the proposed change. Merely fetching a new ETag and resending the old computed body can still lose the other client's edit. ETags are not monotonic generation counters; matching one is not proof that no intervening write occurred.
A content validator is not a metadata revision
S3 documents that an ETag reflects changes to object contents, not metadata. A metadata-only change is therefore outside the protection offered by comparing that content validator. Do not use a successful ETag comparison as evidence that every property consulted by your application is unchanged. [2]
A constructed example makes the distinction explicit: a client reads bytes and metadata, another writer changes only metadata, and the first client later submits a body computed from its old view. Comparing the content ETag alone does not validate that old metadata. This is a consequence of the documented scope, not a live S3 test. See compare-and-swap for the related distinction between comparing values and comparing revisions.
Multipart completion is a decision point
CompleteMultipartUpload accepts these write conditions too. If it returns 409 ConditionalRequestConflict, AWS specifies a new multipart upload and re-upload of all parts, not just replaying completion with the old upload ID. The initial HTTP 200 can also contain an embedded error: raw clients must inspect the response body; AWS SDKs handle that case. [7]
An in-progress multipart upload does not reserve the destination key. In a versioned bucket, conditions concern the current version: a current delete marker allows If-None-Match: * even when older versions remain. A missing current object or delete marker makes If-Match fail with 404. [8]
Versioning retains recoverable versions. Our inference from that contract is that it does not merge concurrent edits or reject a stale writer on its own; retaining an overwritten version and preventing an overwrite are different policies. [11]
Research bridge: the commit is more than an uploaded file
Armbrust et al.'s Delta Lake paper separates new data objects from the log record that commits a table version. Its 2020 design writes data first, then atomically claims the next log position; readers use the log to determine table membership. Merely uploading valid bytes is not the table commit. [12, section 3.2]
The paper's statement that S3 lacked atomic put-if-absent is historical, not a description of today's S3 API: AWS announced conditional creation in August 2024. [13] This does not certify a present-day Delta deployment or imply that one conditional PUT implements its transaction protocol.
Engineering lesson: locate the publication decision and its conflict handling, rather than inferring transaction guarantees from object checksums or successful uploads. See storage consistency and transaction scope.
Keep the checksum attached to the right object
GetObject exposes checksum fields when checksum mode is enabled, with Base64 values and checksum-type metadata. Bind comparisons to the same object version, covered bytes, algorithm and type. A checksum retrieved as metadata is not evidence that the client has hashed its downloaded copy. [9]
Digest verification is not independent authentication. If both content and the expected digest can be replaced, an unprotected comparison can pass on the wrong content. A trusted integrity channel and an appropriate algorithm are still needed. [10]
Practical review: name the intended key or version, the write condition, the checksum coverage and the recovery decision separately. Preserve uncertainty when a request's outcome is unknown. See timeouts and retry identity and checksums, identity and lost writes.
Corpus records: entity tag, multipart upload, conditional write and object storage.
Primary sources and limits
Reviewed 12 October 2026. S3-specific statements here cover general-purpose buckets, PutObject and CompleteMultipartUpload; they do not certify other S3-compatible implementations, directory buckets or CopyObject preconditions. The worked example is synthetic, not a live service test.
- RFC 9110: HTTP Semantics, sections 8.8.3 and 13.1.1
- Amazon S3 API: Object, ETag semantics and encryption exceptions
- Amazon S3: checking object integrity during upload
- Amazon S3: multipart upload with additional checksums, ETag verification example
- Amazon S3: data consistency model
- Amazon S3 API: PutObject, If-Match and If-None-Match
- Amazon S3 API: CompleteMultipartUpload, conditional conflicts and embedded errors
- Amazon S3: conditional-write behavior and current-version semantics
- Amazon S3 API: GetObject, ChecksumMode and checksum response fields
- RFC 9530: Digest Fields, section 6.1, integrity and authentication
- Amazon S3: using versioning in S3 buckets
- Armbrust et al. (2020), Delta Lake: High-Performance ACID Table Storage over Cloud Object Stores, PVLDB 13(12), 3411-3424; section 3.2; DOI 10.14778/3415478.3415560
- AWS, 20 August 2024: Amazon S3 now supports conditional writes