hashicorp/terraform · error

server returned unsuccessful status

Error message

server returned unsuccessful status %d

What it means

In `AvailableVersions`, after handling 200, 404, 401/403, any other HTTP status falls through to the `default` arm and is wrapped as `ErrQueryFailed` with the numeric status. It represents an unexpected response from the network mirror's version index endpoint.

Solutions

  1. Reproduce with `curl -i https://<mirror>/<provider>/index.json` to see the raw status and body.
  2. If 429/5xx, retry after a delay; check mirror health and capacity.
  3. Confirm the mirror URL and provider path are correct; fix the mirror backend.
  4. If the mirror is permanently broken, remove its `network_mirror` block and fall back to the default registry.

Example fix

// before: 502 from mirror
terraform init // server returned unsuccessful status 502
// after: hit the endpoint directly, fix upstream, or drop the mirror
$ curl -i https://mirror.local/hashicorp/aws/index.json
# once 200, re-run init
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight the version index endpoint
resp, err := http.Get(indexURL)
if err != nil { return err }
defer resp.Body.Close()
if resp.StatusCode != 200 {
    return fmt.Errorf("mirror index not ready: %d", resp.StatusCode)
}

Try / catch

// Retry on 5xx/429
err := retryDo(ctx, 3, backoff, func() error {
    _, e := src.AvailableVersions(ctx, provider)
    return e
})

Prevention

When it happens

Trigger: `AvailableVersions` issues GET to the mirror's `<provider>/index.json`; `statusCode` is not in {200,404,401,403}; default at http_mirror_source.go:137.

Common situations: Mirror returns 5xx (server error, gateway timeout); 418/451 or other unusual status codes; rate limiting (429) from a CDN-fronted mirror; broken backend behind the mirror URL.

Related errors


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

Appendix: source

Thrown at internal/getproviders/http_mirror_source.go:137

		if body != nil {
			body.Close()
		}
	}()
	if err != nil {
		return nil, nil, s.errQueryFailed(provider, err)
	}

	switch statusCode {
	case http.StatusOK:
		// Great!
	case http.StatusNotFound:
		return nil, nil, ErrProviderNotFound{
			Provider: provider,
		}
	case http.StatusUnauthorized, http.StatusForbidden:
		return nil, nil, s.errUnauthorized(finalURL)
	default:
		return nil, nil, s.errQueryFailed(provider, fmt.Errorf("server returned unsuccessful status %d", statusCode))
	}

	// If we got here then the response had status OK and so our body
	// will be non-nil and should contain some JSON for us to parse.
	var bodyContent ListVersionsResponseBody

	dec := json.NewDecoder(body)
	if err := dec.Decode(&bodyContent); err != nil {
		return nil, nil, s.errQueryFailed(provider, fmt.Errorf("invalid response content from mirror server: %s", err))
	}

	if len(bodyContent.Versions) == 0 {
		return nil, nil, nil
	}
	ret := make(VersionList, 0, len(bodyContent.Versions))
	for versionStr := range bodyContent.Versions {
		version, err := ParseVersion(versionStr)
		if err != nil {

View on GitHub (pinned to d32a084675)