hashicorp/terraform · error
the string provided in credentials is neither valid json…
Error message
the string provided in credentials is neither valid json nor a valid file path
What it means
Thrown when the credentials string was read successfully (either as a file's contents or as literal text) but the result is not valid JSON. The backend mirrors the provider behavior of accepting either a file path or inline JSON, and after reading it validates with json.Valid before handing it to option.WithCredentialsJSON.
Solutions
- Open the credentials content and run it through a JSON linter to locate the syntax error.
- Re-download the service-account JSON key from the GCP console (IAM & Admin > Service accounts > Keys).
- Ensure the file contains a single JSON object starting with '{' and ending with '}'.
- Confirm you are not accidentally providing an OAuth token or API key string.
Example fix
// before
export GOOGLE_CREDENTIALS='{ "type": "service_account" ... (truncated) }'
// after — re-export the complete key
gcloud iam service-accounts keys create sa.json --iam-account=terraform@proj.iam.gserviceaccount.com
export GOOGLE_CREDENTIALS=$(cat sa.json) Defensive patterns
Strategy: validation
Validate before calling
contents, err := readPathOrContents(creds)
if err != nil { return err }
if !json.Valid([]byte(contents)) {
return fmt.Errorf("credentials content failed json.Valid; re-download the service-account key")
} Type guard
func isServiceAccountJSON(s string) bool {
if !json.Valid([]byte(s)) { return false }
var m map[string]interface{}
_ = json.Unmarshal([]byte(s), &m)
return m["type"] == "service_account" && m["private_key"] != nil
} Prevention
- Re-download keys from the GCP console rather than hand-editing.
- Validate the key with `jq . service-account.json` before exporting it.
- Never paste partial JSON; always the complete object.
When it happens
Trigger: readPathOrContents returns content that fails json.Valid — e.g., the file is not a service-account key, contains trailing garbage, was truncated, or the 'path' was actually a JSON blob missing braces.
Common situations: User pastes a key ID or project ID instead of the full JSON key; the JSON file has a BOM or stray newline outside the object; the value is a base64-encoded credential instead of raw JSON.
Related errors
- Error loading credentials
- Error decoding encryption key
- Error loading encryption key
- impersonate_service_account_delegates elements must not be…
- sasToken cannot be empty
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/b38cac87adc1faac.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/gcs/backend.go:204
} else {
creds = os.Getenv("GOOGLE_CREDENTIALS")
}
if tokenSource != nil {
credOptions = append(credOptions, option.WithTokenSource(tokenSource))
} else if creds != "" {
// to mirror how the provider works, we accept the file path or the contents
contents, err := readPathOrContents(creds)
if err != nil {
return backendbase.ErrorAsDiagnostics(
fmt.Errorf("Error loading credentials: %s", err),
)
}
if !json.Valid([]byte(contents)) {
return backendbase.ErrorAsDiagnostics(
fmt.Errorf("the string provided in credentials is neither valid json nor a valid file path"),
)
}
credOptions = append(credOptions, option.WithCredentialsJSON([]byte(contents)))
}
// Service Account Impersonation
if v := data.String("impersonate_service_account"); v != "" {
ServiceAccount := v
var delegates []string
delegatesVal := data.GetAttr("impersonate_service_account_delegates", cty.List(cty.String))
if !delegatesVal.IsNull() && delegatesVal.LengthInt() != 0 {
delegates = make([]string, 0, delegatesVal.LengthInt())
for it := delegatesVal.ElementIterator(); it.Next(); {
_, v := it.Element()
if v.IsNull() {
return backendbase.ErrorAsDiagnostics(View on GitHub (pinned to d32a084675)