{"record":{"id":"c392c45df5c77865","repo":"juicedata/juicefs","slug":"no-etag","errorCode":null,"errorMessage":"No ETag","messagePattern":"No ETag","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/object/ufile.go","lineNumber":300,"sourceCode":"\t}\n\treturn &MultipartUpload{UploadID: out.UploadId, MinPartSize: int64(out.BlkSize), MaxCount: 1000000}, nil\n}\n\nfunc (u *ufile) UploadPart(ctx context.Context, key string, uploadID string, num int, data []byte) (*Part, error) {\n\t// UFile require the PartNumber to start from 0 (continuous)\n\tnum--\n\tpath := fmt.Sprintf(\"%s?uploadId=%s&partNumber=%d\", key, uploadID, num)\n\tresp, err := u.request(ctx, \"PUT\", path, bytes.NewReader(data), nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cleanup(resp)\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"UploadPart: %s\", parseError(resp).Error())\n\t}\n\tetags := resp.Header[\"Etag\"]\n\tif len(etags) < 1 {\n\t\treturn nil, errors.New(\"No ETag\")\n\t}\n\treturn &Part{Num: num, Size: len(data), ETag: strings.Trim(etags[0], \"\\\"\")}, nil\n}\n\nfunc (u *ufile) AbortUpload(ctx context.Context, key string, uploadID string) {\n\t_, _ = u.request(ctx, \"DELETE\", key+\"?uploads=\"+uploadID, nil, nil)\n}\n\nfunc (u *ufile) CompleteUpload(ctx context.Context, key string, uploadID string, parts []*Part) error {\n\tetags := make([]string, len(parts))\n\tfor i, p := range parts {\n\t\tetags[i] = p.ETag\n\t}\n\tresp, err := u.request(ctx, \"POST\", key+\"?uploadId=\"+uploadID, bytes.NewReader([]byte(strings.Join(etags, \",\"))), nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cleanup(resp)","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/object/ufile.go#L282-L318","documentation":"ufile.UploadPart expects the UCloud UFile response to carry an ETag header for each uploaded part; the ETag is required to complete the multipart upload. If the response has no Etag header, the part result cannot be built and the upload fails.","triggerScenarios":"Calling UploadPart against a UFile endpoint whose successful (200) response omits the Etag header — e.g. a proxy/gateway stripping headers, or a non-standard endpoint returning 200 with an error body.","commonSituations":"Corporate proxies or CDNs in front of UFile that strip headers; misconfigured UFile bucket domains; region/endpoint mismatches returning unusual responses.","solutions":["Verify the bucket's domain/endpoint points directly at UFile and no proxy strips the Etag header","Re-run the upload; transient gateway issues may drop headers","Check response with curl to confirm the Etag header is present at your endpoint","Capture the raw HTTP response for debugging and report to UFile support if the service omits ETag"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"resp, _ := http.Head(endpoint)\nif resp.Header.Get(\"Etag\") == \"\" {\n\t// endpoint/proxy strips headers — fix config before uploading\n}","typeGuard":null,"tryCatchPattern":"part, err := store.UploadPart(ctx, key, uploadID, num, data)\nif err != nil && strings.Contains(err.Error(), \"No ETag\") {\n\t// retry once; else fail with clear diagnostics\n\tpart, err = store.UploadPart(ctx, key, uploadID, num, data)\n}","preventionTips":["Point UFile buckets at native UFile endpoints, not header-stripping proxies","Test endpoint responses with curl before large multipart uploads","Monitor for repeated No ETag failures indicating infrastructure changes"],"tags":["ufile","multipart-upload","http-response"],"backgroundTag":"unexpected-response-shape","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}