From fe78b559d161a3fab4d596c8125c1594bf447882 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Thu, 18 Jun 2026 17:46:26 +0100 Subject: [PATCH] yandex: fix 500 errors by waiting for uploads to complete before setting modtime After PUTting a file to the upload URL, Yandex keeps the file locked for writing until the upload operation finishes committing on the server. The PUT returned before this happened, so the following SetModTime raced the still-in-progress write and got spurious 500 Internal Server Error responses. Capture the operation_id returned with the upload URL and poll the operation status until it reports success before returning, so the file is fully committed before we access it. --- backend/yandex/api/types.go | 7 ++++--- backend/yandex/yandex.go | 10 +++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/backend/yandex/api/types.go b/backend/yandex/api/types.go index b1da038da..24c06514d 100644 --- a/backend/yandex/api/types.go +++ b/backend/yandex/api/types.go @@ -52,9 +52,10 @@ type ResourceListResponse struct { // AsyncInfo struct is returned by the API for various async operations. type AsyncInfo struct { - HRef string `json:"href"` - Method string `json:"method"` - Templated bool `json:"templated"` + HRef string `json:"href"` + Method string `json:"method"` + Templated bool `json:"templated"` + OperationID string `json:"operation_id"` } // AsyncStatus is returned when requesting the status of an async operations. Possible values in-progress, success, failure diff --git a/backend/yandex/yandex.go b/backend/yandex/yandex.go index 3edbfccd1..d767fd176 100644 --- a/backend/yandex/yandex.go +++ b/backend/yandex/yandex.go @@ -577,7 +577,7 @@ func (f *Fs) waitForJob(ctx context.Context, location string) (err error) { } switch status.Status { - case "failure": + case "failure", "failed": return fmt.Errorf("async operation returned %q", status.Status) case "success": return nil @@ -1124,6 +1124,14 @@ func (o *Object) upload(ctx context.Context, in io.Reader, overwrite bool, mimeT resp, err = o.fs.srv.Call(ctx, &opts) return shouldRetry(ctx, resp, err) }) + if err != nil { + return err + } + + // Wait for PUT to be committed + if ur.OperationID != "" { + err = o.fs.waitForJob(ctx, rootURL+"/operations/"+ur.OperationID) + } return err }