hashicorp/terraform · error

Error loading credentials

Error message

Error loading credentials: %s

What it means

Thrown when the GOOGLE_CREDENTIALS value (or the credentials config attribute) points to a path or literal content that readPathOrContents cannot resolve. readPathOrContents accepts either a filesystem path or raw content, and this error means neither interpretation succeeded (e.g., the file does not exist or is unreadable).

Solutions

  1. Verify the path exists: check the value of GOOGLE_CREDENTIALS and stat the file it names.
  2. If passing inline JSON, paste the full service-account JSON rather than a path.
  3. Fix file permissions so the process running terraform can read it (e.g., chmod 600 service-account.json).
  4. Use an absolute path to avoid working-directory resolution issues.

Example fix

// before
export GOOGLE_CREDENTIALS=./creds/sa.json   # relative, wrong cwd
// after
export GOOGLE_CREDENTIALS=/absolute/path/to/sa.json
Defensive patterns

Strategy: validation

Validate before calling

// Validate the credentials source resolves before Configure.
creds := os.Getenv("GOOGLE_CREDENTIALS")
if creds == "" {
    creds = /* config attr */ ""
}
if creds != "" {
    if _, err := readPathOrContents(creds); err != nil {
        return fmt.Errorf("credentials source is not a readable path or content: %w", err)
    }
}

Prevention

When it happens

Trigger: Configure runs, creds is non-empty, and readPathOrContents(creds) returns an error — typically a missing file, a permission denied on the file, or a malformed path string.

Common situations: GOOGLE_CREDENTIALS points to a relative path resolved from the wrong working directory; the service account JSON was deleted; the file has restrictive permissions; the value is a typo'd path rather than JSON content.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/404bb1d757d14bc2. Report an issue: GitHub.

Appendix: source

Thrown at internal/backend/remote-state/gcs/backend.go:198

			AccessToken: v,
		})
	} else if v := data.String("credentials"); v != "" {
		creds = v
	} else if v := os.Getenv("GOOGLE_BACKEND_CREDENTIALS"); v != "" {
		creds = v
	} 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))

View on GitHub (pinned to d32a084675)