anomalyco/sst · error

failed to copy directory %s: %w

Error message

failed to copy directory %s: %w

What it means

copySourceFilesSimple locates the top-level source directory of the handler (e.g. `services/api/functions/handler.ts` → first path component that exists as a dir in the workspace) and recursively copies it into the build output. This error is returned when that copyDir fails. It wraps the underlying OS error from copying a file inside that directory.

Source

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

	}

	outputBase := input.Out()
	// For container builds, source files go at root of build context (Dockerfile expects it).
	// For zip builds, preserve the nested directory structure via outputPrefix.
	if outputPrefix != "" && !input.IsContainer {
		outputBase = filepath.Join(input.Out(), outputPrefix)
	}

	if strings.Contains(handlerPath, "/") {
		// Find the top-level directory that exists in the workspace
		parts := strings.Split(handlerPath, "/")
		copied := false
		for i := 0; i < len(parts)-1; i++ {
			candidate := parts[i]
			candidatePath := filepath.Join(workspaceDir, candidate)
			if info, err := os.Stat(candidatePath); err == nil && info.IsDir() {
				if err := copyDir(candidatePath, filepath.Join(outputBase, candidate), skipContent); err != nil {
					return fmt.Errorf("failed to copy directory %s: %w", candidate, err)
				}
				copied = true
				break
			}
		}
		if !copied {
			// Handler path fully resolved by workspaceDir — root .py files will be copied below
		}
	}

	// Also copy root-level .py files
	entries, err := os.ReadDir(workspaceDir)
	if err != nil {
		return fmt.Errorf("failed to read workspace directory: %w", err)
	}
	for _, entry := range entries {
		if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".py") {
			if err := copyFile(filepath.Join(workspaceDir, entry.Name()), filepath.Join(outputBase, entry.Name())); err != nil {

View on GitHub (pinned to a0bd20f762)

Solutions

  1. Check the wrapped error for the failing filename; restore permissions (`chmod -R u+r <dir>`) or recreate the missing file.
  2. Stop concurrent watchers/generators, then rerun the deploy.
  3. Clean the build output (`rm -rf .sst`) and redeploy to clear conflicting destinations.
  4. Verify free disk space on the volume holding the build output.

Example fix

// before
Error: failed to copy source files: failed to copy directory functions: open functions/handler.py: permission denied
// after
$ chmod -R u+r functions/
$ sst deploy
Defensive patterns

Strategy: try-catch

Validate before calling

fs.accessSync(handlerDir, fs.constants.R_OK);
fs.accessSync(buildOut, fs.constants.W_OK);

Type guard

null

Try / catch

try {
  await deploy();
} catch (e) {
  if (/failed to copy directory/.test(e.message)) {
    const m = e.message.match(/failed to copy directory (\S+): (.*)/);
    console.error(`Fix perms/missing file in ${m?.[1]}: ${m?.[2]}`);
    throw e;
  } else throw e;
}

Prevention

When it happens

Trigger: copyDir(candidatePath, filepath.Join(outputBase, candidate), skipContent) fails in copySourceFilesSimple — a source file disappears mid-copy, a file is unreadable (permissions), or the destination cannot be written.

Common situations: Handler source directory contains files owned by another user; a file is removed by a code generator/watcher during deploy; read-only source mount; output directory full.

Related errors


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