hashicorp/terraform · error

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

Error message

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

What it means

Returned by getWithGoGetter (getter.go:133) when, after successfully creating the destination directory for a cached-module reuse, copy.CopyDir(prevDir, instPath) fails. The reusingGetter copies the previously-fetched module tree rather than re-downloading; a copy failure means the cached source tree could not be reproduced at the destination.

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 c9def3e214)

Solutions

  1. Read the copy error in the message: 'no such file' → the cached prevDir was removed; 'permission denied' → fix read perms on prevDir; 'no space' → free disk.
  2. Run terraform init -upgrade to discard the stale/partial cache and re-download fresh.
  3. Remove the .terraform/modules cache directory and re-run init so the source tree is re-fetched instead of copied.
  4. Serialize parallel terraform runs sharing the same cache to avoid races.

Example fix

# before — cache hit but prevDir was deleted by a parallel run

# after — clear cache and re-fetch
rm -rf .terraform/modules
terraform init
Defensive patterns

Strategy: retry

Validate before calling

// Confirm the cached source tree still exists before reuse
func cacheIntact(prevDir string) bool {
    info, err := os.Stat(prevDir)
    return err == nil && info.IsDir()
}

Type guard

func cacheIntact(prevDir string) bool {
    info, err := os.Stat(prevDir)
    return err == nil && info.IsDir()
}

Try / catch

if err := getter.Get(ctx, ...); err != nil {
    if strings.Contains(err.Error(), "failed to copy from") {
        // cache likely stale — clear and re-fetch fresh
        os.RemoveAll(filepath.Dir(prevDir))
        err = getter.Get(ctx, ...)
    }
    return err
}

Prevention

When it happens

Trigger: A module packageAddr was already fetched once (cache hit in reusingGetter), the mkdir succeeded, but copying the cached directory tree failed. The message reports prevDir, instPath, and the copy error.

Common situations: The previously-cached module directory (prevDir) was deleted or moved between the cache hit and the copy (race with cleanup). Disk full mid-copy. Permission mismatch where prevDir is readable but a sub-file is not. Filesystem errors on network-mounted volumes. Concurrent runs mutating the cache.

Related errors


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