hashicorp/terraform · error

response has invalid Content-Type: must be application/json

Error message

response has invalid Content-Type: must be application/json

What it means

After successfully parsing the `Content-Type`, the client asserts the parsed media type is exactly `application/json`. Any other media type (e.g. `text/html`, `application/octet-stream`) on a 200 response triggers this error.

Solutions

  1. Configure the mirror to send `Content-Type: application/json` for index responses.
  2. Check for default-catch-all pages served by the mirror host.
  3. `curl -i` the URL and confirm the header.

Example fix

// before
Content-Type: text/html
...<html>...
// after
Content-Type: application/json
{"versions":{...}}
Defensive patterns

Strategy: validation

Validate before calling

// Verify the served media type
mt, _, _ := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if mt != "application/json" {
    return fmt.Errorf("mirror content type %q is not application/json", mt)
}

Type guard

// isJSONMediaType narrows to application/json responses
func isJSONMediaType(ct string) bool {
    mt, _, err := mime.ParseMediaType(ct)
    return err == nil && mt == "application/json"
}

Prevention

When it happens

Trigger: `mime.ParseMediaType` succeeds but `mt != "application/json"`; error at http_mirror_source.go:380.

Common situations: Mirror serves an HTML page with status 200; mirror serves JSON as `text/plain`; CDN/default fallback returns a generic page; misconfigured reverse proxy default type.

Related errors


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

Appendix: source

Thrown at internal/getproviders/http_mirror_source.go:380

		if body == nil {
			resp.Body.Close()
		}
	}()
	// After this point, our final URL return value should always be the
	// one from resp.Request, because that takes into account any redirects
	// we followed along the way.
	finalURL = resp.Request.URL

	if resp.StatusCode == http.StatusOK {
		// If and only if we get an OK response, we'll check that the response
		// type is JSON and return the body reader.
		ct := resp.Header.Get("Content-Type")
		mt, params, err := mime.ParseMediaType(ct)
		if err != nil {
			return 0, nil, finalURL, fmt.Errorf("response has invalid Content-Type: %s", err)
		}
		if mt != "application/json" {
			return 0, nil, finalURL, fmt.Errorf("response has invalid Content-Type: must be application/json")
		}
		for name := range params {
			// The application/json content-type has no defined parameters,
			// but some servers are configured to include a redundant "charset"
			// parameter anyway, presumably out of a sense of completeness.
			// We'll ignore them but warn that we're ignoring them in case the
			// subsequent parsing fails due to the server trying to use an
			// unsupported character encoding. (RFC 7159 defines its own
			// JSON-specific character encoding rules.)
			log.Printf("[WARN] Network mirror returned %q as part of its JSON content type, which is not defined. Ignoring.", name)
		}
		body = resp.Body
	}

	return resp.StatusCode, body, finalURL, nil
}

func (s *HTTPMirrorSource) errQueryFailed(provider addrs.Provider, err error) error {

View on GitHub (pinned to d32a084675)