hashicorp/terraform · error

Error reading

Error message

Error reading %s: %s

What it means

Thrown when Terraform cannot read a CLI configuration file (.terraformrc or .tfrc) at the filesystem level. ioutil.ReadFile returns an error which is wrapped with the file path. This is distinct from parsing errors (549, 550) — the file could not even be opened for reading.

Solutions

  1. Verify the config file path exists: ls -la <path>
  2. Check file permissions: chmod 644 ~/.terraformrc
  3. Ensure TF_CLI_CONFIG_FILE (if set) points to an existing, readable file
  4. Check for broken symlinks in the config path
Defensive patterns

Strategy: validation

Validate before calling

// Check config file exists and is readable before relying on it
func checkConfigFile(path string) error {
    info, err := os.Stat(path)
    if err != nil {
        return fmt.Errorf("config file %s not accessible: %w", path, err)
    }
    if info.IsDir() {
        return fmt.Errorf("config path %s is a directory, not a file", path)
    }
    return nil
}

Prevention

When it happens

Trigger: loadConfigFile(path) calls ioutil.ReadFile(path) which fails because the file does not exist at the resolved path, permissions are denied, the path points to a directory, or an I/O error occurs.

Common situations: TF_CLI_CONFIG_FILE points to a nonexistent file; ~/.terraformrc has wrong permissions; file was deleted or moved; running as a different user whose HOME differs; broken symlinks in the config path.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/6f7c6c07f35bbca8. Report an issue: GitHub.

Appendix: source

Thrown at internal/command/cliconfig/cliconfig.go:160

		config = envConfig.Merge(config)
	}

	diags = diags.Append(config.Validate())

	return config, diags
}

// loadConfigFile loads the CLI configuration from ".terraformrc" files.
func loadConfigFile(path string) (*Config, tfdiags.Diagnostics) {
	var diags tfdiags.Diagnostics
	result := &Config{}

	log.Printf("Loading CLI configuration from %s", path)

	// Read the HCL file and prepare for parsing
	d, err := ioutil.ReadFile(path)
	if err != nil {
		diags = diags.Append(fmt.Errorf("Error reading %s: %s", path, err))
		return result, diags
	}

	// Parse it
	obj, err := hcl.Parse(string(d))
	if err != nil {
		diags = diags.Append(fmt.Errorf("Error parsing %s: %s", path, err))
		return result, diags
	}

	// Build up the result
	if err := hcl.DecodeObject(&result, obj); err != nil {
		diags = diags.Append(fmt.Errorf("Error parsing %s: %s", path, err))
		return result, diags
	}

	// Deal with the provider_installation block, which is not handled using
	// DecodeObject because its structure is not compatible with the

View on GitHub (pinned to d32a084675)