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

  1. Open the credentials content and run it through a JSON linter to locate the syntax error.
  2. Re-download the service-account JSON key from the GCP console (IAM & Admin > Service accounts > Keys).
  3. Ensure the file contains a single JSON object starting with '{' and ending with '}'.
  4. 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

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


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)