anomalyco/sst · error

failed to move src directory contents: %w

Error message

failed to move src directory contents: %w

What it means

After removing the old target in the src-layout path, os.Rename(srcPath, targetDir) moves src/{package_name} to its final location. If the rename fails the error is wrapped as "failed to move src directory contents". Renames fail across filesystem boundaries (EXDEV), when the destination exists, or on permission problems.

Source

Thrown at pkg/runtime/python/build.go:461

			}
			out.Close()
		}
	}
	return nil
}

// moveExtractedPackage moves the extracted package to the correct location
func moveExtractedPackage(extractedDir, targetDir, baseName string) error {
	// For src layout, flatten src/{package_name} to {package_name}
	srcPath := filepath.Join(extractedDir, "src", baseName)
	if _, err := os.Stat(srcPath); err == nil {
		if err := os.RemoveAll(targetDir); err != nil {
			return fmt.Errorf("failed to remove old directory: %w", err)
		}

		// Move src/{package_name} to target
		if err := os.Rename(srcPath, targetDir); err != nil {
			return fmt.Errorf("failed to move src directory contents: %w", err)
		}

		// Clean up extracted directory
		if err := os.RemoveAll(extractedDir); err != nil {
			return fmt.Errorf("failed to clean up extracted directory: %w", err)
		}
	} else {
		// No src directory — check if package needs flattening
		if shouldFlattenPackage(extractedDir) {
			return flattenPackageToRoot(extractedDir, targetDir)
		}

		// Standard case: rename directory
		if err := os.RemoveAll(targetDir); err != nil {
			return fmt.Errorf("failed to remove old directory: %w", err)
		}

		if err := os.Rename(extractedDir, targetDir); err != nil {

View on GitHub (pinned to a0bd20f762)

Solutions

  1. Ensure extractedDir and targetDir share the same filesystem/volume (use a work dir next to the target)
  2. Run only one build at a time for the same project to avoid races on targetDir
  3. Verify parent directory write permissions for the build user
  4. If cross-device moves are unavoidable, switch to a copy-then-delete strategy instead of rename
  5. Retry after transient mount issues (e.g. EBUSY on network volumes)

Example fix

// before: rename across devices fails with EXDEV
os.Rename(srcPath, targetDir)

// after: fall back to copy when rename fails
if err := os.Rename(srcPath, targetDir); err != nil {
    if err := copyDir(srcPath, targetDir); err != nil {
        return fmt.Errorf("failed to move src directory contents: %w", err)
    }
    os.RemoveAll(srcPath)
}
Defensive patterns

Strategy: try-catch

Validate before calling

sameDevice := func(a, b string) bool {
    var s1, s2 syscall.Stat_t
    if syscall.Stat(a, &s1) != nil || syscall.Stat(filepath.Dir(b), &s2) != nil {
        return false
    }
    return s1.Dev == s2.Dev
}
if !sameDevice(extractedDir, targetDir) {
    // use copy-then-delete instead of rename
}

Try / catch

if err := processPackageArchive(...); err != nil {
    var linkErr *os.LinkError
    if errors.As(err, &linkErr) && linkErr.Err == syscall.EXDEV {
        // fall back to copying the directory
    }
    return err
}

Prevention

When it happens

Trigger: extractedDir and targetDir are on different mounts/filesystems (os.Rename cannot cross devices), targetDir was partially recreated between RemoveAll and Rename, or a parent directory is not writable.

Common situations: Build artifacts in /tmp while the target lives on a Docker volume (different devices); two concurrent builds racing to install the same package; sandboxed CI runners restricting rename operations.

Related errors


AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30). Data as JSON: /api/errors/1091cc21a9d62ec6. Report an issue: GitHub.