AlistGo/alist · error
finish upload failed: %s
Error message
finish upload failed: %s
What it means
Returned by completeMultipartUpload when the 'phase=finish&uploadmode=part' POST answers with a code other than 2000 (success) or 4024 (merge-in-progress). The server refused to finalize the merged object; message is the server's reason. Note 4024 is intentionally tolerated because merging is asynchronous.
Source
Thrown at drivers/doubao/util.go:742
body := _convertUploadParts(parts)
err := utils.Retry(MaxRetryAttempts, time.Second, func() (err error) {
_, err = d.uploadRequest(uploadUrl, http.MethodPost, storeInfo, func(req *resty.Request) {
req.SetQueryParams(map[string]string{
"uploadid": uploadID,
"phase": "finish",
"uploadmode": "part",
})
req.SetBody(body)
}, &uploadResp)
if err != nil {
return err
}
// 检查响应状态码 2000 成功 4024 分片合并中
if uploadResp.Code != 2000 && uploadResp.Code != 4024 {
return fmt.Errorf("finish upload failed: %s", uploadResp.Message)
}
return err
})
if err != nil {
return fmt.Errorf("failed to complete multipart upload: %w", err)
}
return nil
}
func (d *Doubao) commitMultipartUpload(uploadConfig *UploadConfig) error {
uploadUrl := d.UploadToken.Samantha.UploadInfo.VideoHost
params := map[string]string{
"Action": "CommitUploadInner",
"Version": "2020-11-19",
"SpaceName": d.UploadToken.Samantha.UploadInfo.SpaceName,View on GitHub (pinned to 843d9dc814)
Solutions
- Inspect the server message — it typically says which parts are missing or whether the session is invalid
- If the session expired, restart the whole upload with a fresh init
- Verify all parts returned non-empty Etags before finish (a skipped/failed part with empty etag corrupts the finish body)
- Retry after backoff; 4024-adjacent states sometimes settle into 2000
- Check driver updates for changes to _convertUploadParts or the finish query params
Defensive patterns
Strategy: retry
Validate before calling
// verify every part has a non-empty Etag before finishing
for i, p := range parts {
if p == nil || p.Etag == "" {
return fmt.Errorf("part %d missing etag; refusing to finish", i+1)
}
} Try / catch
if err := d.completeMultipartUpload(...); err != nil {
if strings.Contains(err.Error(), "finish upload failed") {
time.Sleep(5 * time.Second) // give async merge (4024) time to settle
return d.completeMultipartUpload(...)
}
return err
} Prevention
- Never finish with gaps in the parts array
- Retry finish once after a delay — async merge states (4024) settle to 2000
- Don't re-init the same upload session from another client mid-flight
When it happens
Trigger: Finishing a multipart upload where some parts were never received server-side despite client-side success, the upload session expired before finish, wrong uploadid, or the finish body (_convertUploadParts serialization) doesn't match the server's record of parts.
Common situations: A part upload silently failed earlier (etag mismatch not caught), very long uploads where the session times out server-side, retry of finish after partial server state, or Doubao changing the finish contract so the parts list encoding is rejected.
Related errors
- init upload failed: %s
- upload part failed: %s
- failed to commit upload: %w
- failed to upload node: %w
- wukong multipart finish failed: code=%d message=%s
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/ed72a643f8e337d7.
Report an issue: GitHub.