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
- Check that the parent directory of instPath exists and is writable by the current user.
- 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.
- Ensure no concurrent terraform processes target the same working directory; serialize or use distinct data directories with -chdir / TF_DATA_DIR.
- 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
- Run terraform as a single user; avoid mixing sudo and non-sudo against the same .terraform directory.
- Pre-create and chown the modules cache directory before init in CI.
- Serialize concurrent terraform runs against the same working directory, or use distinct TF_DATA_DIR per job.
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
- bucket not exists
- cannot create temporary file to update credentials
- Cannot read directory
- cannot read
- cannot search
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)