{"record":{"id":"110824baac5e955c","repo":"juicedata/juicefs","slug":"unexpected-etag-s-s","errorCode":null,"errorMessage":"unexpected ETag: %s != %s","messagePattern":"unexpected ETag: (.+?) != (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/object/obs.go","lineNumber":178,"sourceCode":"\tmimeType := utils.GuessMimeType(key)\n\tparams := &obs.PutObjectInput{}\n\tparams.Bucket = s.bucket\n\tparams.Key = key\n\tparams.Body = body\n\tparams.ContentLength = vlen\n\tparams.ContentMD5 = base64.StdEncoding.EncodeToString(sum[:])\n\tparams.ContentType = mimeType\n\tt := s.getRuntimeTier(ctx)\n\tparams.StorageClass = obs.StorageClassType(t.Sc)\n\tvar resp *obs.PutObjectOutput\n\tvar err error\n\tif t.encodedTag != \"\" {\n\t\tresp, err = s.c.PutObject(params, obs.WithHeader(obsTaggingHeader, []string{t.encodedTag}))\n\t} else {\n\t\tresp, err = s.c.PutObject(params)\n\t}\n\tif err == nil && s.checkEtag && strings.Trim(resp.ETag, \"\\\"\") != obs.Hex(sum) {\n\t\terr = fmt.Errorf(\"unexpected ETag: %s != %s\", strings.Trim(resp.ETag, \"\\\"\"), obs.Hex(sum))\n\t}\n\tif resp != nil {\n\t\tattrs := ApplyGetters(getters...)\n\t\tattrs.SetRequestID(resp.RequestId).SetStorageClass(getOrDefaultScValue(string(resp.StorageClass), string(obs.StorageClassStandard)))\n\t}\n\treturn err\n}\n\nfunc (s *obsClient) Copy(ctx context.Context, dst, src string) error {\n\tt := s.getRuntimeTier(ctx)\n\tsc := getOrDefaultScValue(t.Sc, string(obs.StorageClassStandard))\n\tparams := &obs.CopyObjectInput{}\n\tparams.Bucket = s.bucket\n\tparams.Key = dst\n\tparams.CopySourceBucket = s.bucket\n\tparams.CopySourceKey = src\n\tparams.StorageClass = obs.StorageClassType(sc)\n\tvar err error","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/object/obs.go#L160-L196","documentation":"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>'.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := store.Put(ctx, key, body); err != nil && strings.Contains(err.Error(), \"unexpected ETag\") {\n\t// re-upload once; if persistent, check bucket encryption settings\n}","preventionTips":["Check bucket server-side encryption before relying on ETag verification","Avoid content-mutating proxies on the upload path","Retry transient mismatches once before alerting"],"tags":["obs","huawei-cloud","etag","integrity"],"backgroundTag":"checksum-mismatch","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}