{"record":{"id":"7a7c8f037c42761b","repo":"hashicorp/terraform","slug":"failed-to-create-directory-s-s","errorCode":null,"errorMessage":"failed to create directory %s: %s","messagePattern":"failed to create directory (.+?): (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/getmodules/getter.go","lineNumber":129,"sourceCode":"// This function would ideally accept packageAddr as a value of type\n// addrs.ModulePackage, but we can't do that because the addrs package\n// depends on this package for package address parsing. Therefore we just\n// use a string here but assume that the caller got that value by calling\n// the String method on a valid addrs.ModulePackage value.\n//\n// The errors returned by this function are those surfaced by the underlying\n// go-getter library, which have very inconsistent quality as\n// end-user-actionable error messages. At this time we do not have any\n// reasonable way to improve these error messages at this layer because\n// the underlying errors are not separately recognizable.\nfunc (g reusingGetter) getWithGoGetter(ctx context.Context, instPath, packageAddr string) error {\n\tvar err error\n\n\tif prevDir, exists := g[packageAddr]; exists {\n\t\tlog.Printf(\"[TRACE] getmodules: copying previous install of %q from %s to %s\", packageAddr, prevDir, instPath)\n\t\terr := os.Mkdir(instPath, os.ModePerm)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create directory %s: %s\", instPath, err)\n\t\t}\n\t\terr = copy.CopyDir(instPath, prevDir)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to copy from %s to %s: %s\", prevDir, instPath, err)\n\t\t}\n\t} else {\n\t\tlog.Printf(\"[TRACE] getmodules: fetching %q to %q\", packageAddr, instPath)\n\t\tclient := getter.Client{\n\t\t\tSrc: packageAddr,\n\t\t\tDst: instPath,\n\t\t\tPwd: instPath,\n\n\t\t\tMode: getter.ClientModeDir,\n\n\t\t\tDetectors:     goGetterNoDetectors, // our caller should've already done detection\n\t\t\tDecompressors: goGetterDecompressors,\n\t\t\tGetters:       goGetterGetters,\n\t\t\tCtx:           ctx,","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/getmodules/getter.go#L111-L147","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before: parent dir missing / wrong ownership\n$ terraform init\n# error: failed to create directory .terraform/modules/xxx\n\n# after\n$ mkdir -p .terraform/modules\n$ sudo chown -R $USER .terraform\n$ terraform init","handlingStrategy":"validation","validationCode":"func canCreateModuleDir(path string) error {\n    parent := filepath.Dir(path)\n    info, err := os.Stat(parent)\n    if err != nil { return fmt.Errorf(\"parent %s missing: %w\", parent, err) }\n    if !info.IsDir() { return fmt.Errorf(\"parent %s is not a directory\", parent) }\n    if f, err := os.CreateTemp(parent, \".writeprobe\"); err != nil {\n        return fmt.Errorf(\"parent %s not writable: %w\", parent, err)\n    } else { f.Close(); os.Remove(f.Name()) }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := getter.getWithGoGetter(ctx, instPath, addr); err != nil {\n    if strings.Contains(err.Error(), \"failed to create directory\") {\n        // Filesystem / permissions issue: clean and retry once.\n        _ = os.RemoveAll(instPath)\n        err = getter.getWithGoGetter(ctx, instPath, addr)\n    }\n    if err != nil { return err }\n}","preventionTips":["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."],"tags":["modules","go-getter","filesystem","permissions","go"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}