hashicorp/terraform · error
Cannot read directory
Error message
Cannot read directory %s
What it means
Thrown by processDir when os.ReadDir returns an error that is NOT os.IsNotExist — i.e., the directory exists but cannot be read. The generic OS error is deliberately simplified because raw ReadDir messages are not end-user-appropriate.
Solutions
- Check permissions on the directory: `ls -ld <path>` and ensure read+execute bits are set for the current user.
- Run terraform as a user with access, or `chmod a+rx <dir>` if appropriate.
- Confirm the path is actually a directory and the filesystem mount is healthy.
Example fix
$ chmod a+rx modules/ && terraform fmt modules/
Defensive patterns
Strategy: validation
Validate before calling
// Verify the directory is readable before fmt.
func dirReadable(p string) bool {
f, err := os.Open(p)
if err != nil { return false }
f.Close()
return true
} Prevention
- Ensure read+execute permissions on module directories.
- Run terraform as a user with directory access.
- Confirm the path is a directory, not a file.
When it happens
Trigger: The directory exists but the user lacks read/execute permission on it; the path is not a directory (e.g., a file passed where a dir was expected after stat); filesystem I/O error; SELinux/AppArmor denial.
Common situations: Permission denied (chmod 000 or owned by another user); a broken NFS mount; security policy blocking directory reads; a regular file mistaken for a directory.
Related errors
- Failed to write
- cannot create temporary file to update credentials
- cannot read
- cannot search
- cannot set mode for credentials file
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/cb6b6b1566d49660.
Report an issue: GitHub.
Appendix: source
Thrown at internal/command/fmt.go:243
}
return diags
}
func (c *FmtCommand) processDir(path string, stdout io.Writer) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
log.Printf("[TRACE] terraform fmt: looking for files in %s", path)
entries, err := os.ReadDir(path)
if err != nil {
switch {
case os.IsNotExist(err):
diags = diags.Append(fmt.Errorf("There is no configuration directory at %s", path))
default:
// ReadDir does not produce error messages that are end-user-appropriate,
// so we'll need to simplify here.
diags = diags.Append(fmt.Errorf("Cannot read directory %s", path))
}
return diags
}
for _, info := range entries {
name := info.Name()
if configs.IsIgnoredFile(name) {
continue
}
subPath := filepath.Join(path, name)
if info.IsDir() {
if c.recursive {
subDiags := c.processDir(subPath, stdout)
diags = diags.Append(subDiags)
}
// We do not recurse into child directories by default because we
// want to mimic the file-reading behavior of "terraform plan", etc,View on GitHub (pinned to d32a084675)