hashicorp/terraform · error

returned from

Error message

%s returned from %s

What it means

Produced by getFile when the HTTP response status is not 200 OK while fetching a registry artifact (checksums document, signature file, etc.). The message is '<status> returned from <host>'. It surfaces the upstream status verbatim so the caller sees, for example, '404 Not Found returned from releases.hashicorp.com'.

Solutions

  1. Match the status: 401/403 -> refresh credentials/token; 404 -> the artifact is absent, re-check provider version; 429/5xx -> retry after backoff.
  2. For private registries, re-login with 'terraform login <host>' to refresh the API token.
  3. If mirror is in use, ensure the mirror sync includes checksums and signature artifacts, not just zip packages.
  4. Raise TF_REGISTRY_DISCOVERY_RETRY and TF_REGISTRY_CLIENT_TIMEOUT if the host is flaky.

Example fix

// before: stale token
$ terraform init
Error: 401 Unauthorized returned from app.terraform.io
// after
$ terraform login app.terraform.io
$ terraform init
Defensive patterns

Strategy: try-catch

Try / catch

// Match on ErrQueryFailed and inspect the wrapped message.
var qf getproviders.ErrQueryFailed
if errors.As(err, &qf) {
    msg := qf.Error()
    if strings.Contains(msg, "401") || strings.Contains(msg, "403") { refreshCreds() }
    if strings.Contains(msg, "404") { recheckProviderVersion() }
    if strings.HasPrefix(msg, "5") || strings.HasPrefix(msg, "429") { retry() }
}

Prevention

When it happens

Trigger: Any non-200 response from httpClient.Get on a fully-resolved registry URL: 404 for a missing artifact, 401/403 for unauthorized access, 5xx for upstream errors, 3xx that exhausted redirect limits.

Common situations: Provider version was yanked but metadata still references it (404); token expired for a private registry (401); rate-limited by registry (429); artifact path changed on the registry backend; mirror missing the specific artifact.

Related errors


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

Appendix: source

Thrown at internal/getproviders/registry_client.go:441

	}
}

func (c *registryClient) errUnauthorized(hostname svchost.Hostname) error {
	return ErrUnauthorized{
		Hostname:        hostname,
		HaveCredentials: c.creds != nil,
	}
}

func (c *registryClient) getFile(url *url.URL) ([]byte, error) {
	resp, err := c.httpClient.Get(url.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("%s returned from %s", resp.Status, HostFromRequest(resp.Request))
	}

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return data, err
	}

	return data, nil
}

// configureDiscoveryRetry configures the number of retries the registry client
// will attempt for requests with retryable errors, like 502 status codes
func configureDiscoveryRetry() {
	discoveryRetry = defaultRetry

	if v := os.Getenv(registryDiscoveryRetryEnvName); v != "" {
		retry, err := strconv.Atoi(v)
		if err == nil && retry > 0 {

View on GitHub (pinned to d32a084675)