grafana/k6 · error
reading request body: %w
Error message
reading request body: %w
What it means
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.
Source
Thrown at internal/cloudapi/provisioning/http_client.go:43
version string // for User-Agent
logger logrus.FieldLogger
}
// NewHTTPClient constructs an HTTPClient for metrics push and notify
// in provisioning mode. version is used for the User-Agent header,
// matching the v1/v6 client convention `k6cloud/<version>`.
func NewHTTPClient(httpClient *http.Client, token, version string, logger logrus.FieldLogger) *HTTPClient {
return &HTTPClient{httpClient: httpClient, token: token, version: version, logger: logger}
}
// Do executes the request with Bearer auth, retries on 5xx and
// transport errors, and decodes the response body into v if non-nil.
func (p *HTTPClient) Do(req *http.Request, v any) error {
// Ensure GetBody is set so the body can be replayed on retries.
if req.Body != nil && req.GetBody == nil {
body, err := io.ReadAll(req.Body)
if err != nil {
return fmt.Errorf("reading request body: %w", err)
}
_ = req.Body.Close()
req.GetBody = func() (io.ReadCloser, error) {
return io.NopCloser(bytes.NewReader(body)), nil
}
req.Body, _ = req.GetBody()
req.ContentLength = int64(len(body))
}
req.Header.Set("Authorization", "Bearer "+p.token)
req.Header.Set("User-Agent", "k6cloud/"+p.version)
resp, err := doWithRetry(p.httpClient, req)
if err != nil {
return err
}
View on GitHub (pinned to 93accf6570)
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
Example fix
// before
req, _ := http.NewRequestWithContext(ctx, "POST", pushURL, failingStream)
err := client.Do(req, &resp)
// after
payload, err := io.ReadAll(failingStream)
if err != nil {
return err
}
req, err := http.NewRequestWithContext(ctx, "POST", pushURL, bytes.NewReader(payload))
if err != nil {
return err
}
err = client.Do(req, &resp) Defensive patterns
Strategy: validation
Validate before calling
payload, err := io.ReadAll(body)
if err != nil {
return fmt.Errorf("buffering request body: %w", err)
}
req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(payload)) Type guard
func isReplayable(req *http.Request) bool {
return req.Body == nil || req.GetBody != nil
} Try / catch
if err := httpClient.Do(req, &out); err != nil {
if strings.Contains(err.Error(), "reading request body") {
// the body stream itself is broken; rebuild the request from a []byte buffer
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Metrics-push or notify requests assembled over streaming/compressed readers instead of byte slices; test harnesses piping bodies through processes that exit early.
Related errors
- can't fetch the page for unknown reason
- waitFor retry threshold reached
- HTTP exporter endpoint is required
- cookie: is null
- no arguments
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/93fb2ffafae042eb.
Report an issue: GitHub.