hashicorp/terraform · error

failed to create directory

Error message

failed to create directory %s: %s

What it means

Returned by reusingGetter.getWithGoGetter when os.Mkdir fails to create the install path for a module copy. The function caches prior downloads keyed by package address; on a cache hit it tries to create a fresh destination directory and copy the prior download into it. If Mkdir fails (permissions, parent missing, path exists), it wraps the OS error with the target path for diagnostics.

Solutions

  1. Check that the parent directory of instPath exists and is writable by the current user.
  2. If ownership is wrong (e.g. root-owned .terraform after sudo), chown the modules directory back to your user, or remove it and let Terraform recreate it.
  3. Ensure no concurrent terraform processes target the same working directory; serialize or use distinct data directories with -chdir / TF_DATA_DIR.
  4. If the filesystem is read-only, point TF_DATA_DIR / the modules cache at a writable location.

Example fix

# before: parent dir missing / wrong ownership
$ terraform init
# error: failed to create directory .terraform/modules/xxx

# after
$ mkdir -p .terraform/modules
$ sudo chown -R $USER .terraform
$ terraform init
Defensive patterns

Strategy: validation

Validate before calling

func canCreateModuleDir(path string) error {
    parent := filepath.Dir(path)
    info, err := os.Stat(parent)
    if err != nil { return fmt.Errorf("parent %s missing: %w", parent, err) }
    if !info.IsDir() { return fmt.Errorf("parent %s is not a directory", parent) }
    if f, err := os.CreateTemp(parent, ".writeprobe"); err != nil {
        return fmt.Errorf("parent %s not writable: %w", parent, err)
    } else { f.Close(); os.Remove(f.Name()) }
    return nil
}

Try / catch

if err := getter.getWithGoGetter(ctx, instPath, addr); err != nil {
    if strings.Contains(err.Error(), "failed to create directory") {
        // Filesystem / permissions issue: clean and retry once.
        _ = os.RemoveAll(instPath)
        err = getter.getWithGoGetter(ctx, instPath, addr)
    }
    if err != nil { return err }
}

Prevention

When it happens

Trigger: Calling getmodules fetch with a previously-downloaded package address whose cached entry exists, where the new instPath cannot be created — e.g. the parent directory does not exist, the caller lacks write permission, or instPath already exists as a file/directory.

Common situations: Stale .terraform directory with wrong ownership after running as a different user; concurrent terraform runs racing to create the same module directory; read-only filesystem mounts for the modules cache; instPath points to a location whose parent was not created by the caller.

Related errors


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

Appendix: source

Thrown at internal/getmodules/getter.go:129

// This function would ideally accept packageAddr as a value of type
// addrs.ModulePackage, but we can't do that because the addrs package
// depends on this package for package address parsing. Therefore we just
// use a string here but assume that the caller got that value by calling
// 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,

View on GitHub (pinned to d32a084675)