hashicorp/terraform · error

the string provided in credentials is neither valid json nor

Error message

the string provided in credentials is neither valid json nor a valid file path

What it means

Raised by the GCS backend when the 'credentials' string is not valid JSON and readPathOrContents did not treat it as a readable file path (no read error), so it is neither a JSON blob nor a loadable file. The backend refuses to pass garbage to the GCS client. This is the catch-all for a malformed credentials value.

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 c9def3e214)

Solutions

  1. Provide the full service-account JSON key (download from GCP IAM > Service accounts > Keys) inline or via a readable file path.
  2. Validate the JSON before using: 'echo "$CREDENTIALS" | jq .' or 'python -m json.tool sa.json'.
  3. Avoid shell mangling: write the key to a file and reference the path, or export it via a heredoc.
  4. Confirm you are not passing the OAuth access_token, email, or project id in the 'credentials' field.

Example fix

# before: pasted email / mangled string -> 219
credentials = "tf-sa@my-project.iam.gserviceaccount.com"
# after: full service-account JSON
credentials = file("/abs/path/sa.json")
# validate first
jq empty /abs/path/sa.json && echo "valid json"
Defensive patterns

Strategy: validation

Validate before calling

// Validate the credentials value is JSON or a path to JSON before init.
func validateCreds(creds string) error {
    if json.Valid([]byte(creds)) { return nil }
    b, err := os.ReadFile(creds)
    if err != nil {
        return fmt.Errorf("the string provided in credentials is neither valid json nor a valid file path")
    }
    if !json.Valid(b) {
        return fmt.Errorf("credentials file %q is not valid JSON", creds)
    }
    return nil
}

Prevention

When it happens

Trigger: At backend.go:202-205: after readPathOrContents succeeds (returns a string), json.Valid([]byte(contents)) is false. Triggered when credentials is a non-JSON string that is also not an existing file path — e.g. a truncated key, a base64 blob, a service-account email, or pasted JSON with stray characters.

Common situations: Pasted the service-account email instead of the key JSON; pasted JSON that lost quotes/newlines through shell escaping; provided a base64-encoded key where raw JSON was expected; credentials file path was actually valid as a string but the file does not exist AND the string is not JSON (note: this fires when readPathOrContents returns the string unchanged because it is not a path).

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/b38cac87adc1faac. Report an issue: GitHub.