{"record":{"id":"cdf50fce14aa9541","repo":"opentofu/opentofu","slug":"failed-to-decode-content-md5-s-w","errorCode":null,"errorMessage":"Failed to decode Content-MD5 '%s': %w","messagePattern":"Failed to decode Content-MD5 '(.+?)': %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/http/client.go","lineNumber":230,"sourceCode":"\tif _, err := io.Copy(buf, resp.Body); err != nil {\n\t\treturn nil, fmt.Errorf(\"Failed to read remote state: %w\", err)\n\t}\n\n\t// Create the payload\n\tpayload := &remote.Payload{\n\t\tData: buf.Bytes(),\n\t}\n\n\t// If there was no data, then return nil\n\tif len(payload.Data) == 0 {\n\t\treturn nil, nil\n\t}\n\n\t// Check for the MD5\n\tif raw := resp.Header.Get(\"Content-MD5\"); raw != \"\" {\n\t\tmd5, err := base64.StdEncoding.DecodeString(raw)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\n\t\t\t\t\"Failed to decode Content-MD5 '%s': %w\", raw, err)\n\t\t}\n\n\t\tpayload.MD5 = md5\n\t} else {\n\t\t// Generate the MD5\n\t\thash := md5.Sum(payload.Data)\n\t\tpayload.MD5 = hash[:]\n\t}\n\n\treturn payload, nil\n}\n\nfunc (c *httpClient) Put(ctx context.Context, data []byte) error {\n\t// Copy the target URL\n\tbase := *c.URL\n\n\tif c.lockID != \"\" {","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/opentofu/opentofu/blob/3561785c48c1ce615e7c50261bd351f26053efa2/internal/backend/remote-state/http/client.go#L212-L248","documentation":"The state response carried a Content-MD5 header that is not valid base64, so the client cannot decode it for integrity checking, and it refuses the payload. Per RFC 1864 the header must be the base64 encoding of the raw 16-byte MD5 digest; sending hex text or any non-base64 value triggers this. The offending header value is included in the message.","triggerScenarios":"A hand-rolled state server that hex-encodes the MD5 (32 hex chars) instead of base64-encoding the raw digest; an intermediary rewriting or corrupting the header; wrong padding/charset in a custom header injection.","commonSituations":"Custom state servers written without an RFC 1864-compliant MD5 helper; debug proxies adding their own Content-MD5; header rewritten by CDN transforms.","solutions":["Fix the server: send base64.StdEncoding.EncodeToString(md5.Sum(data)) in Go, or the equivalent in the server's language","Alternatively omit the Content-MD5 response header entirely — the client then computes the MD5 locally","Verify what is actually sent: curl -I \"$ADDRESS\" and try 'echo <value> | base64 -d' to confirm decodability"],"exampleFix":"// state server (Go), before\nw.Header().Set(\"Content-MD5\", hex.EncodeToString(md5bytes))\n\n// after\nw.Header().Set(\"Content-MD5\", base64.StdEncoding.EncodeToString(md5bytes))","handlingStrategy":"validation","validationCode":"# Pre-flight: confirm the server's Content-MD5 decodes as base64\nval=$(curl -sI \"$ADDRESS\" | awk -F': ' 'tolower($1)==\"content-md5\" {print $2}' | tr -d '\\r')\nif [ -n \"$val\" ] && ! echo \"$val\" | base64 -d >/dev/null 2>&1; then\n  echo \"Content-MD5 is not valid base64: $val\"; exit 1\nfi","typeGuard":null,"tryCatchPattern":null,"preventionTips":["On the server, send base64(raw 16-byte MD5 digest) per RFC 1864, or omit the header entirely","Add a response-header conformance test to the state server's CI","Watch for proxies that rewrite content headers"],"tags":["content-md5","header","base64","integrity"],"backgroundTag":null,"analyzedSha":"3561785c48c1ce615e7c50261bd351f26053efa2","analyzedAt":"2026-08-15T23:27:16.226Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}