hashicorp/terraform · error
credentials file has invalid value for "credentials"…
Error message
credentials file %s has invalid value for "credentials" property: must be a JSON object
What it means
Thrown after the credentials file parsed as JSON but its top-level "credentials" property is not a JSON object (map). Terraform requires `credentials` to be an object keyed by hostname. If it is an array, string, number, or null, the type assertion `rawCredsI.(map[string]interface{})` fails and this error fires before any mutation.
Solutions
- Inspect the file: `jq .type ~/.terraform.d/credentials.tfrc.json` and `jq '.credentials | type'` — it must report `object`.
- Rewrite the credentials property as an object, e.g. `{"credentials": {"app.terraform.io": {"token": "..."}}}`.
- If unsure of the correct shape, back up the file and run `terraform login` to regenerate it.
- Audit any external tool or helper writing the file against the documented schema.
Example fix
// before (invalid):
// { "credentials": "abc123" }
// after (valid):
// {
// "credentials": {
// "app.terraform.io": { "token": "abc123" }
// }
// } Defensive patterns
Strategy: type-guard
Type guard
// Guard that the parsed credentials file has the expected object shape
func credentialsShapeOK(path string) error {
var raw map[string]interface{}
data, err := os.ReadFile(path)
if err != nil { return err }
if err := json.Unmarshal(data, &raw); err != nil { return err }
c, ok := raw["credentials"]
if !ok { return nil } // missing is fine — code creates it
if _, ok := c.(map[string]interface{}); !ok {
return fmt.Errorf("credentials property must be a JSON object")
}
return nil
} Prevention
- Treat the credentials file schema as fixed: top-level object with a 'credentials' object.
- Validate third-party helper output before installing it.
- Use `jq '.credentials | type' file` to confirm 'object'.
When it happens
Trigger: Credentials file is valid JSON but has shape like `{"credentials": "..."}`, `{"credentials": [ ... ]}`, or `{"credentials": 123}`. Typically from a hand edit, a broken migration, or a tool that wrote a different schema.
Common situations: User confused the legacy `~/.terraformrc` (which stores helper config differently) with `credentials.tfrc.json`; a credentials helper emitted the wrong top-level shape; a copy-paste from documentation that used the wrong key.
Related errors
- Can't serialize backend configuration as JSON
- Failed to set state store configuration
- can not get from Terraform backend configuration
- Can't serialize backend configuration as JSON
- cannot create temporary file to update credentials
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/88cd2c47f5e87ebe.
Report an issue: GitHub.
Appendix: source
Thrown at internal/command/cliconfig/credentials.go:358
// 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"]
if !ok {
rawCredsI = make(map[string]interface{})
raw["credentials"] = rawCredsI
}
rawCredsMap, ok := rawCredsI.(map[string]interface{})
if !ok {
return fmt.Errorf("credentials file %s has invalid value for \"credentials\" property: must be a JSON object", filename)
}
// We use display-oriented hostnames in our file to mimick how a human user
// would write it, so we need to search for and remove any key that
// normalizes to our target hostname so we won't generate something invalid
// when the existing entry is slightly different.
for givenHost := range rawCredsMap {
canonHost, err := svchost.ForComparison(givenHost)
if err == nil && canonHost == host {
delete(rawCredsMap, givenHost)
}
}
// If we have a new object to store we'll write it in now. If the previous
// object had the hostname written in a different way then this will
// appear to change it into our canonical display form, with all the
// letters in lowercase and other transforms from the Internationalized
// Domain Names specification.View on GitHub (pinned to d32a084675)