{"record":{"id":"93fb2ffafae042eb","repo":"grafana/k6","slug":"reading-request-body-w","errorCode":null,"errorMessage":"reading request body: %w","messagePattern":"reading request body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cloudapi/provisioning/http_client.go","lineNumber":43,"sourceCode":"\tversion    string // for User-Agent\n\tlogger     logrus.FieldLogger\n}\n\n// NewHTTPClient constructs an HTTPClient for metrics push and notify\n// in provisioning mode. version is used for the User-Agent header,\n// matching the v1/v6 client convention `k6cloud/<version>`.\nfunc NewHTTPClient(httpClient *http.Client, token, version string, logger logrus.FieldLogger) *HTTPClient {\n\treturn &HTTPClient{httpClient: httpClient, token: token, version: version, logger: logger}\n}\n\n// Do executes the request with Bearer auth, retries on 5xx and\n// transport errors, and decodes the response body into v if non-nil.\nfunc (p *HTTPClient) Do(req *http.Request, v any) error {\n\t// Ensure GetBody is set so the body can be replayed on retries.\n\tif req.Body != nil && req.GetBody == nil {\n\t\tbody, err := io.ReadAll(req.Body)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"reading request body: %w\", err)\n\t\t}\n\t\t_ = req.Body.Close()\n\n\t\treq.GetBody = func() (io.ReadCloser, error) {\n\t\t\treturn io.NopCloser(bytes.NewReader(body)), nil\n\t\t}\n\t\treq.Body, _ = req.GetBody()\n\t\treq.ContentLength = int64(len(body))\n\t}\n\n\treq.Header.Set(\"Authorization\", \"Bearer \"+p.token)\n\treq.Header.Set(\"User-Agent\", \"k6cloud/\"+p.version)\n\n\tresp, err := doWithRetry(p.httpClient, req)\n\tif err != nil {\n\t\treturn err\n\t}\n","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/cloudapi/provisioning/http_client.go#L25-L61","documentation":"HTTPClient.Do (internal/cloudapi/provisioning/http_client.go:43) reads the request body into memory to install req.GetBody, which is required so the body can be replayed on 5xx/transport retries. This error means the caller-supplied body reader itself failed while being read — the request never went on the wire.","triggerScenarios":"An *http.Request built over a custom io.Reader that returns an error mid-read (a pipe whose writer closed, a failing decompression stream, a file that vanished), passed to Do with GetBody unset.","commonSituations":"Metrics-push or notify requests assembled over streaming/compressed readers instead of byte slices; test harnesses piping bodies through processes that exit early.","solutions":["Buffer the payload into []byte and build the request with bytes.NewReader so the body is trivially replayable","Fix or check the underlying reader's error source (file handle, pipe writer, decompressor)","Set req.GetBody yourself when the body is already replayable, which skips the buffering path entirely"],"exampleFix":"// before\nreq, _ := http.NewRequestWithContext(ctx, \"POST\", pushURL, failingStream)\nerr := client.Do(req, &resp)\n\n// after\npayload, err := io.ReadAll(failingStream)\nif err != nil {\n\treturn err\n}\nreq, err := http.NewRequestWithContext(ctx, \"POST\", pushURL, bytes.NewReader(payload))\nif err != nil {\n\treturn err\n}\nerr = client.Do(req, &resp)","handlingStrategy":"validation","validationCode":"payload, err := io.ReadAll(body)\nif err != nil {\n\treturn fmt.Errorf(\"buffering request body: %w\", err)\n}\nreq, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(payload))","typeGuard":"func isReplayable(req *http.Request) bool {\n\treturn req.Body == nil || req.GetBody != nil\n}","tryCatchPattern":"if err := httpClient.Do(req, &out); err != nil {\n\tif strings.Contains(err.Error(), \"reading request body\") {\n\t\t// the body stream itself is broken; rebuild the request from a []byte buffer\n\t}\n\treturn err\n}","preventionTips":["Build request bodies from []byte/bytes.Buffer, not live streams","Set GetBody when constructing requests over custom readers","Check producer health (files, pipes) before the request is assembled"],"tags":["http","request-body","io","retry","buffering"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}