{"record":{"id":"4e559da1048deda0","repo":"kubernetes/kops","slug":"error-moving-downloaded-file-q-to-q-v","errorCode":null,"errorMessage":"error moving downloaded file %q to %q: %v","messagePattern":"error moving downloaded file %q to %q: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/http.go","lineNumber":77,"sourceCode":"\toutput, err := os.CreateTemp(dir, \".\"+filepath.Base(destPath)+\".tmp\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error creating temporary file for download %q: %v\", destPath, err)\n\t}\n\ttempPath := output.Name()\n\tdefer os.Remove(tempPath)\n\n\tactual, err := downloadURLToWriter(ctx, url, output, hash)\n\tif closeErr := output.Close(); closeErr != nil && err == nil {\n\t\terr = closeErr\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := os.Chmod(tempPath, 0o644); err != nil {\n\t\treturn nil, fmt.Errorf(\"error setting mode on downloaded file %q: %v\", tempPath, err)\n\t}\n\tif err := os.Rename(tempPath, destPath); err != nil {\n\t\treturn nil, fmt.Errorf(\"error moving downloaded file %q to %q: %v\", tempPath, destPath, err)\n\t}\n\treturn actual, nil\n}\n\n// downloadURLToWriter streams the file at the given url to dest.\n// If hash is non-nil, it will also verify that it matches the downloaded bytes.\nfunc downloadURLToWriter(ctx context.Context, desturl string, dest io.Writer, hash *hashing.Hash) (*hashing.Hash, error) {\n\tu, err := url.Parse(desturl)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Invalud URL for file %q: %v\", desturl, err)\n\t}\n\n\tstart := time.Now()\n\tdefer func() {\n\t\tklog.V(2).Infof(\"Downloading %q took %q\", desturl, time.Since(start))\n\t}()\n\tklog.V(2).Infof(\"Downloading %q\", desturl)\n","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/http.go#L59-L95","documentation":"The final step makes the download atomic: the temp file is renamed over the destination path. If os.Rename fails, this error reports both the temp path and the destination. This guarantees partial downloads never replace a good file.","triggerScenarios":"os.Rename(tempPath, destPath) fails: cross-device link (temp and dest on different filesystems — not possible here since temp is in the same dir), dest directory removed mid-flight, or permission problems.","commonSituations":"Destination directory deleted or unmounted during download; a directory exists at destPath; antivirus/cleanup tooling interfering with .tmp files.","solutions":["Check the wrapped %v error (EXDEV, ENOENT, EACCES etc.)","Verify the destination directory still exists and is writable","Remove any directory occupying the destination path","Re-run the download"],"exampleFix":"// before: /path/to/file is actually a directory\nrm -rf /path/to/file\n// after: re-run kops apply to retry the download","handlingStrategy":"try-catch","validationCode":"if info, err := os.Lstat(destPath); err == nil && info.IsDir() {\n    return fmt.Errorf(\"destination %s is a directory\", destPath)\n}","typeGuard":null,"tryCatchPattern":"if _, err := fi.DownloadURL(ctx, url, dest, nil); err != nil {\n    var le *os.LinkError\n    if errors.As(err, &le) {\n        log.Printf(\"rename failed: %v -> %v: %v\", le.Old, le.New, le.Err)\n    }\n    return err\n}","preventionTips":["Never place a directory where the destination file is expected","Avoid unmounting or deleting download directories mid-operation","Keep the download destination stable across runs"],"tags":["filesystem","rename","download"],"backgroundTag":"rename-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}