hashicorp/terraform · error
cannot read
Error message
cannot read %s: %s
What it means
Thrown by CredentialsSource.updateLocalHostCredentials when ioutil.ReadFile fails to read the credentials JSON file (e.g. ~/.terraform.d/credentials.tfrc.json) with an error other than os.IsNotExist. It is the first I/O gate in updating a host's credentials: any read error that is not 'file does not exist' (permission denied, I/O error, path-too-long) is surfaced here before parsing.
Solutions
- Verify the path Terraform resolves for the credentials file: run `terraform -help` style debugging or print s.CredentialsFilePath() — usually ~/.terraform.d/credentials.tfrc.json or $TF_CLI_CONFIG_FILE/credentials.tfrc.json.
- Check permissions/ownership: `ls -l <path>` and `stat <path>`; ensure the current uid can read it (chmod u+r or chown).
- Confirm the path is a regular file, not a directory or broken symlink (`readlink -f <path>`).
- If the file is corrupt/unwanted, back it up and remove it so Terraform recreates it on next login.
Example fix
// before: file owned by root, user cannot read // $ sudo chown $USER:$USER ~/.terraform.d/credentials.tfrc.json // $ chmod 600 ~/.terraform.d/credentials.tfrc.json // after: terraform login succeeds and updateLocalHostCredentials reads the file
Defensive patterns
Strategy: validation
Validate before calling
// Before calling any credentials-updating path, confirm the file is readable
func credentialsFileReadable(path string) error {
fi, err := os.Stat(path)
if os.IsNotExist(err) {
return nil // not-exist is tolerated by updateLocalHostCredentials
}
if err != nil {
return fmt.Errorf("stat %s: %w", path, err)
}
if fi.IsDir() {
return fmt.Errorf("%s is a directory, not a credentials file", path)
}
f, err := os.Open(path)
if err != nil {
return fmt.Errorf("%s not readable: %w", path, err)
}
f.Close()
return nil
} Prevention
- Run Terraform as the user who owns ~/.terraform.d/credentials.tfrc.json.
- Never chmod the credentials file to deny its owner read access.
- Set TF_CLI_CONFIG_FILE to an explicit, owned, regular file in automation.
When it happens
Trigger: Calling any Terraform command that persists credentials (terraform login / logout, or the credentials helper update path) when the credentials file exists but is unreadable. Specifically: file mode bits deny read to the current uid; the path points to a directory; the volume is detached; or the file is on a network mount returning EIO.
Common situations: File created by a different user/root with 0600 perms so the running user cannot read it; TF_CLI_CONFIG_FILE or TF_CREDENTIALS pointing at a stale path; a previous crash left a half-named file; container volume mount permission mismatch.
Related errors
- cannot create temporary file to update credentials
- cannot set mode for credentials file
- failed to initialize cloudplugin cache directory
- can not get from Terraform backend configuration
- Cannot read directory
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/b02e217a0437d193.
Report an issue: GitHub.
Appendix: source
Thrown at internal/command/cliconfig/credentials.go:333
default:
// Should never happen because the above cases are exhaustive
return fmt.Errorf("invalid credentials location %#v", loc)
}
}
func (s *CredentialsSource) updateLocalHostCredentials(host svchost.Hostname, new svcauth.HostCredentialsWritable) error {
// This function updates the local credentials file in particular,
// regardless of whether a credentials helper is active. It should be
// called only indirectly via updateHostCredentials.
filename, err := s.CredentialsFilePath()
if err != nil {
return fmt.Errorf("unable to determine credentials file path: %s", err)
}
oldSrc, err := ioutil.ReadFile(filename)
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("cannot read %s: %s", filename, err)
}
var raw map[string]interface{}
if len(oldSrc) > 0 {
// When decoding we use a custom decoder so we can decode any numbers as
// json.Number and thus avoid losing any accuracy in our round-trip.
dec := json.NewDecoder(bytes.NewReader(oldSrc))
dec.UseNumber()
err = dec.Decode(&raw)
if err != nil {
return fmt.Errorf("cannot read %s: %s", filename, err)
}
} else {
raw = make(map[string]interface{})
}
rawCredsI, ok := raw["credentials"]View on GitHub (pinned to d32a084675)