## TL;DR

Refresh failed, so Terraform could not reconcile state with reality. This is a connectivity or permission problem with the provider's API, not drift and not your config. Fix the network path or credentials, then re-run `plan`. Never apply after a failed refresh: the plan is invalid.

## The error

```text
Error: Failed to refresh state: ... Unable to connect to the remote API
```

## Steps to fix

1. Read the detail after the colon: DNS failure, TLS error, 401/403, or timeout tells you which layer broke.
   - Expected: you know whether it is network or auth.
2. Verify the API is reachable with the provider's own CLI (`aws sts get-caller-identity`, `az account show`, `gcloud auth list`).
   - Expected: the same failure appears outside Terraform, confirming it is environmental.
3. Fix it: VPN/proxy for network issues, fresh credentials or `sso login` for auth issues.
   - Expected: the CLI check succeeds.
4. Re-run `terraform plan` (with refresh enabled).
   - Expected: refresh completes and planning proceeds.

## When to use this

- `terraform plan` fails during the refresh phase with `Failed to refresh state`, especially in CI, on VPN, or after credential rotation.

## When NOT to use this

- Do not paper over it with `-refresh=false`: that plans against stale state, hides drift, and can produce changes a refresh would have cancelled. Use it only to isolate whether the failure is refresh-related.

## Compatibility

- All Terraform versions; refresh behavior is stable (refresh-only plans, `-refresh-only`, since 1.x).

## Root cause

Every plan starts by refreshing state against real APIs. If the API is unreachable (network, proxy, DNS) or rejects the credentials, refresh cannot complete, and Terraform aborts rather than plan against state it knows is stale.

## Edge cases

- Intermittent API flakiness: retry; refresh failures are safe to retry because nothing was written.
- A single broken resource can fail the whole refresh; `terraform plan -target` is not the answer, fix the resource or its credentials.
- `terraform plan -refresh-only` is the safe way to detect drift on a schedule; it still needs the backend lock and provider read permissions.