{"record":{"id":"15583d0ab720eae9","repo":"anomalyco/sst","slug":"failed-to-rename-directory-w","errorCode":null,"errorMessage":"failed to rename directory: %w","messagePattern":"failed to rename directory: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/runtime/python/build.go","lineNumber":480,"sourceCode":"\t\t}\n\n\t\t// Clean up extracted directory\n\t\tif err := os.RemoveAll(extractedDir); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to clean up extracted directory: %w\", err)\n\t\t}\n\t} else {\n\t\t// No src directory — check if package needs flattening\n\t\tif shouldFlattenPackage(extractedDir) {\n\t\t\treturn flattenPackageToRoot(extractedDir, targetDir)\n\t\t}\n\n\t\t// Standard case: rename directory\n\t\tif err := os.RemoveAll(targetDir); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to remove old directory: %w\", err)\n\t\t}\n\n\t\tif err := os.Rename(extractedDir, targetDir); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to rename directory: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// shouldFlattenPackage checks if the directory contains root-level Python files\nfunc shouldFlattenPackage(extractedDir string) bool {\n\tentries, err := os.ReadDir(extractedDir)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tfor _, entry := range entries {\n\t\tif !entry.IsDir() && strings.HasSuffix(entry.Name(), \".py\") {\n\t\t\treturn true\n\t\t}\n\t}","sourceCodeStart":462,"sourceCodeEnd":498,"githubUrl":"https://github.com/anomalyco/sst/blob/a0bd20f762883e72a35caccb4896c42ce5b3f707/pkg/runtime/python/build.go#L462-L498","documentation":"After removing the old target, os.Rename(extractedDir, targetDir) puts the extracted package in its final location. If the rename fails the error is wrapped as \"failed to rename directory\". The most common cause is moving across filesystem boundaries (EXDEV); permissions or a re-created destination also trigger it.","triggerScenarios":"extractedDir (often under the OS temp dir) and targetDir live on different devices/mounts; targetDir reappeared between RemoveAll and Rename (concurrent build); parent dirs not writable by the build user.","commonSituations":"Temp on tmpfs while output is on a Docker volume; CI runners with parallel jobs installing the same package concurrently; sandboxed environments restricting rename syscalls.","solutions":["Place the extraction work dir on the same filesystem as targetDir","Serialize builds that write the same target directory (add locking / run jobs sequentially)","Verify write permission on targetDir's parent directory","Fall back to copy-then-delete when rename is not possible","Retry if the failure was a transient mount error"],"exampleFix":"// before\nif err := os.Rename(extractedDir, targetDir); err != nil {\n    return fmt.Errorf(\"failed to rename directory: %w\", err)\n}\n\n// after: handle cross-device rename\nif err := os.Rename(extractedDir, targetDir); err != nil {\n    if linkErr, ok := err.(*os.LinkError); ok && linkErr.Err == syscall.EXDEV {\n        if err := copyDir(extractedDir, targetDir); err != nil {\n            return fmt.Errorf(\"failed to copy directory: %w\", err)\n        }\n        os.RemoveAll(extractedDir)\n        return nil\n    }\n    return fmt.Errorf(\"failed to rename directory: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"var s1, s2 syscall.Stat_t\nif syscall.Stat(extractedDir, &s1) == nil {\n    if pi, err := os.Stat(filepath.Dir(targetDir)); err == nil {\n        if ps, ok := pi.Sys().(*syscall.Stat_t); ok && ps.Dev != s1.Dev {\n            // different filesystems: use copy-then-delete instead of rename\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := processPackageArchive(...); err != nil {\n    var linkErr *os.LinkError\n    if errors.As(err, &linkErr) && errors.Is(linkErr.Err, syscall.EXDEV) {\n        // fall back to recursive copy\n    }\n    return err\n}","preventionTips":["Extract work dirs on the same filesystem as the final target","Serialize builds that write the same target directory","Grant write access to the target parent directory","Prefer copy fallbacks in environments with overlay/Bind mounts"],"tags":["filesystem","rename","python","packaging"],"backgroundTag":"cross-device-rename","analyzedSha":"a0bd20f762883e72a35caccb4896c42ce5b3f707","analyzedAt":"2026-08-30T11:26:00.383Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}