hashicorp/terraform · error

failed to copy from to

Error message

failed to copy from %s to %s: %s

What it means

Thrown by reusingGetter.getWithGoGetter after the install target directory was created successfully but copy.CopyDir failed copying a previously-cached module install (prevDir) into the new install path (instPath). The first %s is the cache source dir, the second the new dest dir, the third the underlying copy error. It indicates a filesystem-level failure during the module-cache reuse optimization, not a network fetch problem.

Solutions

  1. Check read permission on the cached source dir and write permission on the install dir: ls -ld <prevDir> <instPath> and ensure the Terraform process owns or can write both.
  2. Free disk space or raise inode quota on the filesystem holding the module install directory (often .terraform).
  3. Clear the module cache / remove .terraform/modules and re-run terraform init so the package is fetched fresh instead of copied.
  4. If on a network/mounted filesystem, verify it is mounted read-write and not saturated; rerun init after remounting.
  5. On Windows, disable or except antivirus from scanning the cache dir, or close backup tools that lock files.

Example fix

# before: cache dir owned by root, terraform runs as user -> copy fails
sudo chown -R $(whoami) .terraform/modules

# then re-run
terraform init
Defensive patterns

Strategy: try-catch

Validate before calling

// Before calling getWithGoGetter, verify prevDir readability and instPath writability:
// import "os"
// if fi, err := os.Stat(prevDir); err != nil || !fi.IsDir() {
//     return fmt.Errorf("cache dir unavailable: %w", err)
// }
// if err := os.MkdirAll(filepath.Dir(instPath), 0o755); err != nil {
//     return err
// }

Try / catch

// Wrap the getter call; on copy failure, fall back to a fresh fetch by removing the cached prevDir entry and retrying once.
// err := g.getWithGoGetter(ctx, instPath, addr)
// if err != nil && strings.Contains(err.Error(), "failed to copy from") {
//     delete(g, addr)            // invalidate stale cache entry
//     err = g.getWithGoGetter(ctx, instPath, addr) // retry once with fresh fetch
// }

Prevention

When it happens

Trigger: A second module call references the same packageAddr already downloaded earlier in the same run; the getter reuses it by copying prevDir -> instDir, but CopyDir returns an error (permissions, read-only source, dest on a full/read-only filesystem, or source dir vanished mid-copy).

Common situations: Module cache directory owned by root/another user while Terraform runs as non-root; NFS or mounted volume with permission restrictions; disk-full CI runner; antivirus/backup process locking files on Windows; cache dir manually deleted or rotated between detection and copy.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/94c48b0e6b86604e. Report an issue: GitHub.

Appendix: source

Thrown at internal/getmodules/getter.go:133

// the String method on a valid addrs.ModulePackage value.
//
// The errors returned by this function are those surfaced by the underlying
// go-getter library, which have very inconsistent quality as
// end-user-actionable error messages. At this time we do not have any
// reasonable way to improve these error messages at this layer because
// the underlying errors are not separately recognizable.
func (g reusingGetter) getWithGoGetter(ctx context.Context, instPath, packageAddr string) error {
	var err error

	if prevDir, exists := g[packageAddr]; exists {
		log.Printf("[TRACE] getmodules: copying previous install of %q from %s to %s", packageAddr, prevDir, instPath)
		err := os.Mkdir(instPath, os.ModePerm)
		if err != nil {
			return fmt.Errorf("failed to create directory %s: %s", instPath, err)
		}
		err = copy.CopyDir(instPath, prevDir)
		if err != nil {
			return fmt.Errorf("failed to copy from %s to %s: %s", prevDir, instPath, err)
		}
	} else {
		log.Printf("[TRACE] getmodules: fetching %q to %q", packageAddr, instPath)
		client := getter.Client{
			Src: packageAddr,
			Dst: instPath,
			Pwd: instPath,

			Mode: getter.ClientModeDir,

			Detectors:     goGetterNoDetectors, // our caller should've already done detection
			Decompressors: goGetterDecompressors,
			Getters:       goGetterGetters,
			Ctx:           ctx,
		}
		err = client.Get()
		if err != nil {
			return err

View on GitHub (pinned to d32a084675)