juicedata/juicefs · error
unexpected ETag: %s != %s
Error message
unexpected ETag: %s != %s
What it means
OBS Put verifies that Huawei OBS actually stored the bytes it claims: when checkEtag is enabled (bucket has no server-side encryption), the returned ETag must equal the hex MD5 of the uploaded body. A mismatch means the object was corrupted, transformed (e.g. encryption), or the response ETag is not an MD5, so Put returns 'unexpected ETag: <got> != <expected>'.
Source
Thrown at pkg/object/obs.go:178
mimeType := utils.GuessMimeType(key)
params := &obs.PutObjectInput{}
params.Bucket = s.bucket
params.Key = key
params.Body = body
params.ContentLength = vlen
params.ContentMD5 = base64.StdEncoding.EncodeToString(sum[:])
params.ContentType = mimeType
t := s.getRuntimeTier(ctx)
params.StorageClass = obs.StorageClassType(t.Sc)
var resp *obs.PutObjectOutput
var err error
if t.encodedTag != "" {
resp, err = s.c.PutObject(params, obs.WithHeader(obsTaggingHeader, []string{t.encodedTag}))
} else {
resp, err = s.c.PutObject(params)
}
if err == nil && s.checkEtag && strings.Trim(resp.ETag, "\"") != obs.Hex(sum) {
err = fmt.Errorf("unexpected ETag: %s != %s", strings.Trim(resp.ETag, "\""), obs.Hex(sum))
}
if resp != nil {
attrs := ApplyGetters(getters...)
attrs.SetRequestID(resp.RequestId).SetStorageClass(getOrDefaultScValue(string(resp.StorageClass), string(obs.StorageClassStandard)))
}
return err
}
func (s *obsClient) Copy(ctx context.Context, dst, src string) error {
t := s.getRuntimeTier(ctx)
sc := getOrDefaultScValue(t.Sc, string(obs.StorageClassStandard))
params := &obs.CopyObjectInput{}
params.Bucket = s.bucket
params.Key = dst
params.CopySourceBucket = s.bucket
params.CopySourceKey = src
params.StorageClass = obs.StorageClassType(sc)
var err errorView on GitHub (pinned to c9a67b23e8)
Solutions
- Check whether the bucket has server-side encryption configured and set expectations accordingly
- Retry the upload; if it persists, inspect for content-altering proxies
- Compare the returned ETag manually to confirm whether it is an MD5 of the content
- Report/upgrade if the OBS SDK changes ETag semantics for your bucket type
Defensive patterns
Strategy: retry
Try / catch
if err := store.Put(ctx, key, body); err != nil && strings.Contains(err.Error(), "unexpected ETag") {
// re-upload once; if persistent, check bucket encryption settings
} Prevention
- Check bucket server-side encryption before relying on ETag verification
- Avoid content-mutating proxies on the upload path
- Retry transient mismatches once before alerting
When it happens
Trigger: Calling Put on an obsStore whose bucket has bucket encryption disabled (checkEtag=true) and the response ETag (quotes stripped) does not equal obs.Hex(sum) of the local body.
Common situations: Bucket has SSE enabled but the encryption probe mis-detected it, so ETag is not an MD5; a proxy/middlebox altering content; OBS SDK returning a non-MD5 ETag; multipart-style uploads where ETag differs from plain MD5.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- bucket %q does not exist
- fail to initialize OBS: %q
- invalid endpoint %s: %q
- cannot get location of bucket %s: %q
- get proxy url for endpoint: %s error: %q
AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06).
Data as JSON: /api/errors/110824baac5e955c.
Report an issue: GitHub.