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

  1. Check permissions on the directory: `ls -ld <path>` and ensure read+execute bits are set for the current user.
  2. Run terraform as a user with access, or `chmod a+rx <dir>` if appropriate.
  3. 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

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


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)