{"record":{"id":"88cd2c47f5e87ebe","repo":"hashicorp/terraform","slug":"credentials-file-s-has-invalid-value-for-credent","errorCode":null,"errorMessage":"credentials file %s has invalid value for \"credentials\" property: must be a JSON object","messagePattern":"credentials file (.+?) has invalid value for \"credentials\" property: must be a JSON object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/command/cliconfig/credentials.go","lineNumber":358,"sourceCode":"\t\t// json.Number and thus avoid losing any accuracy in our round-trip.\n\t\tdec := json.NewDecoder(bytes.NewReader(oldSrc))\n\t\tdec.UseNumber()\n\t\terr = dec.Decode(&raw)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot read %s: %s\", filename, err)\n\t\t}\n\t} else {\n\t\traw = make(map[string]interface{})\n\t}\n\n\trawCredsI, ok := raw[\"credentials\"]\n\tif !ok {\n\t\trawCredsI = make(map[string]interface{})\n\t\traw[\"credentials\"] = rawCredsI\n\t}\n\trawCredsMap, ok := rawCredsI.(map[string]interface{})\n\tif !ok {\n\t\treturn fmt.Errorf(\"credentials file %s has invalid value for \\\"credentials\\\" property: must be a JSON object\", filename)\n\t}\n\n\t// We use display-oriented hostnames in our file to mimick how a human user\n\t// would write it, so we need to search for and remove any key that\n\t// normalizes to our target hostname so we won't generate something invalid\n\t// when the existing entry is slightly different.\n\tfor givenHost := range rawCredsMap {\n\t\tcanonHost, err := svchost.ForComparison(givenHost)\n\t\tif err == nil && canonHost == host {\n\t\t\tdelete(rawCredsMap, givenHost)\n\t\t}\n\t}\n\n\t// If we have a new object to store we'll write it in now. If the previous\n\t// object had the hostname written in a different way then this will\n\t// appear to change it into our canonical display form, with all the\n\t// letters in lowercase and other transforms from the Internationalized\n\t// Domain Names specification.","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/command/cliconfig/credentials.go#L340-L376","documentation":"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.","triggerScenarios":"Storing/forgetting credentials when the file's `credentials` field is a scalar or array, e.g. `{ \"credentials\": \"atlasv1...\" }` or `{ \"credentials\": [ ... ] }`.","commonSituations":"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.","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."],"exampleFix":"// before\n{ \"credentials\": \"atlasv1.something\" }\n// credentials file ... has invalid value for \"credentials\" property: must be a JSON object\n\n// after\n{\n  \"credentials\": {\n    \"app.terraform.io\": { \"token\": \"atlasv1.something\" }\n  }\n}","handlingStrategy":"type-guard","validationCode":"// Ensure the 'credentials' property is a JSON object before Terraform touches it.\nfunc credsPropertyIsObject(path string) error {\n    b, err := os.ReadFile(path)\n    if err != nil { return nil } // absent file is fine\n    var v map[string]interface{}\n    if err := json.Unmarshal(b, &v); err != nil { return nil } // handled by 557\n    c, ok := v[\"credentials\"]\n    if !ok { return nil }\n    if _, ok := c.(map[string]interface{}); !ok {\n        return fmt.Errorf(\"'credentials' must be a JSON object, got %T\", c)\n    }\n    return nil\n}","typeGuard":"// Type guard for the credentials property after decode.\nfunc isCredentialsObject(raw map[string]interface{}) bool {\n    c, ok := raw[\"credentials\"]\n    if !ok { return true } // absent is valid\n    _, isObj := c.(map[string]interface{})\n    return isObj\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["terraform","credentials","json","schema","type-mismatch"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}