hashicorp/terraform · error
server returned unsuccessful status %d
Error message
server returned unsuccessful status %d
What it means
Inside AvailableVersions, after handling 200/404/401/403 explicitly, any other status code from the mirror's index.json request is reported via errQueryFailed with this message. It covers server-side failures (5xx) and unexpected 2xx/3xx that slipped through. Returned wrapped as ErrQueryFailed.
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 c9def3e214)
Solutions
- Retry the operation after a short backoff — 5xx/429 are often transient.
- Check the mirror service health and logs for the failing path.
- If 429, raise rate limits or stagger concurrent terraform runs against the mirror.
Example fix
// before
vers, _, err := mirror.AvailableVersions(ctx, p)
if err != nil { return err } // surfaces "server returned unsuccessful status 503"
// after
vers, _, err := retryOn5xx(ctx, func() (getproviders.VersionList, getproviders.Warnings, error) {
return mirror.AvailableVersions(ctx, p)
}) Defensive patterns
Strategy: retry
Try / catch
var qf getproviders.ErrQueryFailed
if errors.As(err, &qf) {
// qf.Wrapped holds the inner "server returned unsuccessful status N" error;
// retry with exponential backoff for transient (5xx/429) statuses
} Prevention
- Wrap AvailableVersions in a bounded retry with backoff for transient statuses.
- Monitor mirror error rates and alert on 5xx spikes.
- Stagger concurrent terraform init runs against a shared mirror.
When it happens
Trigger: GET <host>/<ns>/<type>/index.json returns 500, 502, 503, 504, 429, or another non-handled status. Happens when the mirror is overloaded, a gateway errors, or an auth-adjacent filter returns 401-like behavior as a different code.
Common situations: Mirror behind a flaky gateway; rate limiting (429) from a shared mirror; transient backend outage during terraform init.
Related errors
- too many redirects
- resp.Status
- resp.Status
- HTTP remote state already locked, failed to read body
- HTTP remote state internal server error
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/2fa820308328287a.
Report an issue: GitHub.