{"record":{"id":"a47d15f15e9a66d1","repo":"hashicorp/terraform","slug":"invalid-response-content-from-mirror-server-s","errorCode":null,"errorMessage":"invalid response content from mirror server: %s","messagePattern":"invalid response content from mirror server: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/getproviders/http_mirror_source.go","lineNumber":146,"sourceCode":"\tcase http.StatusOK:\n\t\t// Great!\n\tcase http.StatusNotFound:\n\t\treturn nil, nil, ErrProviderNotFound{\n\t\t\tProvider: provider,\n\t\t}\n\tcase http.StatusUnauthorized, http.StatusForbidden:\n\t\treturn nil, nil, s.errUnauthorized(finalURL)\n\tdefault:\n\t\treturn nil, nil, s.errQueryFailed(provider, fmt.Errorf(\"server returned unsuccessful status %d\", statusCode))\n\t}\n\n\t// If we got here then the response had status OK and so our body\n\t// will be non-nil and should contain some JSON for us to parse.\n\tvar bodyContent ListVersionsResponseBody\n\n\tdec := json.NewDecoder(body)\n\tif err := dec.Decode(&bodyContent); err != nil {\n\t\treturn nil, nil, s.errQueryFailed(provider, fmt.Errorf(\"invalid response content from mirror server: %s\", err))\n\t}\n\n\tif len(bodyContent.Versions) == 0 {\n\t\treturn nil, nil, nil\n\t}\n\tret := make(VersionList, 0, len(bodyContent.Versions))\n\tfor versionStr := range bodyContent.Versions {\n\t\tversion, err := ParseVersion(versionStr)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"[WARN] Ignoring invalid %s version string %q in provider mirror response\", provider, versionStr)\n\t\t\tcontinue\n\t\t}\n\t\tret = append(ret, version)\n\t}\n\n\tret.Sort()\n\treturn ret, nil, nil\n}","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/getproviders/http_mirror_source.go#L128-L164","documentation":"After a 200 response to the versions index, the body is JSON-decoded into ListVersionsResponseBody ({\"versions\": {...}}). If decoding fails the request is failed with errQueryFailed wrapping this message. It means the mirror served a 200 with a body that is not valid JSON or does not match the protocol shape.","triggerScenarios":"Mirror returns 200 but the body is HTML (an error page from a proxy), truncated JSON, a BOM/encoding issue, or a JSON object missing the \"versions\" key with extra junk that breaks the decoder. The decode error is appended verbatim.","commonSituations":"Reverse proxy serves a custom error page with status 200; mirror generated by a script that emitted invalid JSON; compression mismatch (gzip body advertised as identity).","solutions":["Fetch the URL directly with curl and validate the body is JSON with a top-level \"versions\" object.","Fix the mirror generator/server to emit the documented ListVersionsResponseBody schema.","Ensure no proxy rewrites the body or injects an error page on 200."],"exampleFix":"# before: mirror served an HTML error page with 200\ncurl -i https://mirror/tf/registry.terraform.io/hashicorp/aws/index.json\n# HTTP/1.1 200  <html>Not Found</html>\n\n# after: serve real JSON\n# HTTP/1.1 200  Content-Type: application/json\n# {\"versions\":{\"5.0.0\":{},\"5.1.0\":{}}}","handlingStrategy":"try-catch","validationCode":"// Preflight: fetch index.json and confirm it decodes as the protocol shape.\nvar body struct{ Versions map[string]struct{} `json:\"versions\"` }\nif err := json.Unmarshal(raw, &body); err != nil {\n    return fmt.Errorf(\"mirror index.json is not valid: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"var qf getproviders.ErrQueryFailed\nif errors.As(err, &qf) && strings.Contains(qf.Error(), \"invalid response content\") {\n    // mirror protocol bug: surface to the mirror operator, do not retry blindly\n}","preventionTips":["Validate mirror JSON with a schema check during mirror generation.","Never serve HTML error pages with status 200.","Write mirror files atomically (temp + rename)."],"tags":["protocol","json","mirror"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}