Repository: rclone/rclone
File: fs/operations/operations.go (Go)

Code before:
							actionTaken = "Copied (new)"
							dst, err = f.Put(ctx, in, wrappedSrc, options...)
						}
						closeErr := in.Close()
						if err == nil {
							newDst = dst
							err = closeErr
						}
					}
				}
			}

			// Move the copied file to its real destination path.
			if copyRename {
				if err == nil {
					if doMove := f.Features().Move; doMove != nil {
						fs.Debugf(newDst, "move to new destination")
						dst, err = doMove(ctx, newDst, remote)
						if err == nil {
							newDst = dst
						}
					}
				}
				// TODO if the above download failed, we're left behind
				// with a .partial-rngstring file.  What's a good way to
				// clean it up in this retry-loop?
			}
		}
		tries++
		if tries >= maxTries {
			break
		}
		// Retry if err returned a retry error
		if fserrors.ContextError(ctx, &err) {
			break
		}
		var retry bool
		if fserrors.IsRetryError(err) || fserrors.ShouldRetry(err) {
			retry = true
		} else if t, ok := pacer.IsRetryAfter(err); ok {
			fs.Debugf(src, "Sleeping for %v (as indicated by the server) to obey Retry-After error: %v", t, err)
			time.Sleep(t)
			retry = true
		}
		if retry {
			fs.Debugf(src, "Received error: %v - low level retry %d/%d", err, tries, maxTries)
			tr.Reset(ctx) // skip incomplete accounting - will be overwritten by retry
			continue
		}
		// otherwise finish
		break

Review comment (suggestion):
This obviously needs to be resolved.  This part just got my head spinning a little as there are many error states in play here.  Cleaning up after the retry-loop is not enough anymore, as we're creating a new random partial file on each iteration.  

Code after:
					} else {
						in := tr.Account(ctx, in0).WithBuffer() // account and buffer the transfer
						var wrappedSrc fs.ObjectInfo = src
						// We try to pass the original object if possible
						if src.Remote() != remotePartial {
							wrappedSrc = fs.NewOverrideRemote(src, remotePartial)
						}
						options := []fs.OpenOption{hashOption}
						for _, option := range ci.UploadHeaders {
							options = append(options, option)
						}
						if ci.MetadataSet != nil {
							options = append(options, fs.MetadataOption(ci.MetadataSet))
						}
						if doUpdate && inplace {
							err = dst.Update(ctx, in, wrappedSrc, options...)
						} else {
							dst, err = f.Put(ctx, in, wrappedSrc, options...)
						}
						if doUpdate {
							actionTaken = "Copied (replaced existing)"
						} else {
							actionTaken = "Copied (new)"
						}
						closeErr := in.Close()
						if err == nil {
							newDst = dst
							err = closeErr
						}
					}
				}
			}
		}
		tries++
		if tries >= maxTries {
			break
		}
		// Retry if err returned a retry error
		if fserrors.ContextError(ctx, &err) {
			break
		}
		var retry bool
		if fserrors.IsRetryError(err) || fserrors.ShouldRetry(err) {
			retry = true
		} else if t, ok := pacer.IsRetryAfter(err); ok {
			fs.Debugf(src, "Sleeping for %v (as indicated by the server) to obey Retry-After error: %v", t, err)
			time.Sleep(t)
			retry = true
		}
		if retry {
			fs.Debugf(src, "Received error: %v - low level retry %d/%d", err, tries, maxTries)