hashicorp/terraform · error

failed to create directory %s: %s

Error message

failed to create directory %s: %s

What it means

Returned by getWithGoGetter (getter.go:129) when, during reuse of a previously-downloaded module package, os.Mkdir(instPath) fails. The reusingGetter caches the first download of a given packageAddr; on subsequent references it tries to mkdir the destination and copy the cached tree rather than re-fetching. A failed mkdir indicates the install path cannot be created.

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

Solutions

  1. Check the OS error in the message: 'permission denied' → fix directory ownership/permissions on the .terraform directory; 'file exists' → remove the conflicting path; 'no space left' → free disk space.
  2. Ensure the working directory and .terraform/ are writable by the terraform process (common in CI/Docker — chown or run as the right user).
  3. Avoid concurrent terraform init/apply against the same working directory; serialize or use isolated workspaces.
  4. Run terraform init -upgrade or remove .terraform/modules to let it recreate the cache cleanly.

Example fix

# before — .terraform/modules owned by root, terraform runs as non-root

# after — make the modules cache writable
chown -R $(id -u):$(id -g) .terraform
terraform init
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check the install path is creatable before calling go-getter reuse
func canCreateDir(p string) bool {
    parent := filepath.Dir(p)
    info, err := os.Stat(parent)
    return err == nil && info.IsDir() && os.IsWritable... // best-effort
}

Try / catch

if err := getter.Get(ctx, ...); err != nil {
    if strings.Contains(err.Error(), "failed to create directory") {
        // retry once after clearing the stale/partial path
        os.RemoveAll(instPath)
        err = getter.Get(ctx, ...)
    }
    return err
}

Prevention

When it happens

Trigger: Terraform references the same module source twice (so the reusingGetter cache hits) and the second install path cannot be created via os.Mkdir. The message reports instPath and the OS error.

Common situations: The .terraform/modules directory or target path is read-only, on a full disk, or has a conflicting non-directory file at instPath. Permission issues in CI containers. A path collision from parallel terraform runs writing to the same module cache. The parent directory was removed out from under the process.

Related errors


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