hashicorp/terraform · error

invalid response content from mirror server

Error message

invalid response content from mirror server: %s

What it means

After a 200 response on the mirror's version index, the body is decoded as JSON into `ListVersionsResponseBody`. If `json.Decode` fails (malformed JSON, schema mismatch, truncated body), the decode error is wrapped and surfaced via `ErrQueryFailed`. It means the mirror server returned non-JSON or structurally invalid JSON.

Solutions

  1. `curl` the index URL and pipe through `jq .` to confirm valid JSON.
  2. Fix the mirror to serve a `ListVersionsResponseBody`-shaped object (`{"versions": {...}}`).
  3. Ensure no proxy rewrites the body; check `Content-Encoding` and decompression.
  4. If the mirror is unfixable, remove the `network_mirror` block.

Example fix

// before: mirror returns HTML
GET /hashicorp/aws/index.json -> 200, <html>...</html>
// after: mirror returns JSON
GET /hashicorp/aws/index.json -> 200
{"versions":{"4.0":{},"4.1":{}}}
Defensive patterns

Strategy: validation

Validate before calling

// Validate served JSON shape before consumers hit it
var probe ListVersionsResponseBody
if err := json.Unmarshal(raw, &probe); err != nil {
    return fmt.Errorf("mirror index JSON invalid: %w", err)
}

Try / catch

// Treat decode errors as retryable / fall back
versions, err := src.AvailableVersions(ctx, provider)
if err != nil && strings.Contains(err.Error(), "invalid response content") {
    versions, err = defaultRegistry.AvailableVersions(ctx, provider)
}

Prevention

When it happens

Trigger: `json.NewDecoder(body).Decode(&bodyContent)` fails for the version listing; error at http_mirror_source.go:146.

Common situations: Mirror serves HTML error page (but with 200) instead of JSON; proxy injecting content; truncated large response; hand-rolled mirror with wrong JSON shape; encoding/charset issue.

Related errors


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

Appendix: source

Thrown at internal/getproviders/http_mirror_source.go:146

	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 {
			log.Printf("[WARN] Ignoring invalid %s version string %q in provider mirror response", provider, versionStr)
			continue
		}
		ret = append(ret, version)
	}

	ret.Sort()
	return ret, nil, nil
}

View on GitHub (pinned to d32a084675)