hashicorp/terraform · error
credentials file %s has invalid value for "credentials" prop
Error message
credentials file %s has invalid value for "credentials" property: must be a JSON object
What it means
Emitted by `updateLocalHostCredentials` (credentials.go:356-358) when the top-level `"credentials"` property of `credentials.tfrc.json` parses as JSON but is not a JSON object — e.g. it is a string, number, or array. Terraform expects `{ "credentials": { "<host>": { ... } } }`; any other shape for the `credentials` key is rejected before any host is written.
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 c9def3e214)
Solutions
- Rewrite the `credentials` value as an object keyed by hostname: `{ "credentials": { "app.terraform.io": { "token": "..." } } }`.
- Validate the shape with `jq '.credentials | type'` — it must print `object`.
- If unsure of contents, back up the file and let `terraform login` recreate it correctly.
Example fix
// before
{ "credentials": "atlasv1.something" }
// credentials file ... has invalid value for "credentials" property: must be a JSON object
// after
{
"credentials": {
"app.terraform.io": { "token": "atlasv1.something" }
}
} Defensive patterns
Strategy: type-guard
Validate before calling
// Ensure the 'credentials' property is a JSON object before Terraform touches it.
func credsPropertyIsObject(path string) error {
b, err := os.ReadFile(path)
if err != nil { return nil } // absent file is fine
var v map[string]interface{}
if err := json.Unmarshal(b, &v); err != nil { return nil } // handled by 557
c, ok := v["credentials"]
if !ok { return nil }
if _, ok := c.(map[string]interface{}); !ok {
return fmt.Errorf("'credentials' must be a JSON object, got %T", c)
}
return nil
} Type guard
// Type guard for the credentials property after decode.
func isCredentialsObject(raw map[string]interface{}) bool {
c, ok := raw["credentials"]
if !ok { return true } // absent is valid
_, isObj := c.(map[string]interface{})
return isObj
} Prevention
- Always structure `credentials` as an object keyed by hostname.
- Check `jq '.credentials | type'` equals `object`.
- Let Terraform create/own this file rather than external tools.
When it happens
Trigger: Storing/forgetting credentials when the file's `credentials` field is a scalar or array, e.g. `{ "credentials": "atlasv1..." }` or `{ "credentials": [ ... ] }`.
Common situations: Hand-editing the file and flattening the structure; an older/incorrect tool writing tokens directly under `credentials`; schema drift from a non-Terraform tool that shares the file.
Related errors
- cannot read %s: %s
- cannot serialize updated credentials file: %s
- unable to build authorizer for Storage API: %+v
- unable to build authorizer for Resource Manager API: %+v
- the string provided in credentials is neither valid json nor
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/88cd2c47f5e87ebe.
Report an issue: GitHub.