{"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/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/command/cliconfig/credentials.go#L340-L376","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (invalid):\n//   { \"credentials\": \"abc123\" }\n// after (valid):\n//   {\n//     \"credentials\": {\n//       \"app.terraform.io\": { \"token\": \"abc123\" }\n//     }\n//   }","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// Guard that the parsed credentials file has the expected object shape\nfunc credentialsShapeOK(path string) error {\n    var raw map[string]interface{}\n    data, err := os.ReadFile(path)\n    if err != nil { return err }\n    if err := json.Unmarshal(data, &raw); err != nil { return err }\n    c, ok := raw[\"credentials\"]\n    if !ok { return nil } // missing is fine — code creates it\n    if _, ok := c.(map[string]interface{}); !ok {\n        return fmt.Errorf(\"credentials property must be a JSON object\")\n    }\n    return nil\n}","tryCatchPattern":null,"preventionTips":["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'."],"tags":["credentials","json","schema","type-mismatch","config"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}