{"record":{"id":"1f9f3a18b30338fc","repo":"hashicorp/nomad","slug":"error-decoding-stats-data-no-reader-body","errorCode":null,"errorMessage":"error decoding stats data: no reader body","messagePattern":"error decoding stats data: no reader body","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/docker/stats.go","lineNumber":146,"sourceCode":"}\n\n// collectDockerStats performs the stats collection from the Docker API. It is\n// split into its own function for the purpose of aiding testing.\nfunc (h *taskHandle) collectDockerStats(ctx context.Context) (*containerapi.StatsResponse, error) {\n\n\tvar stats *containerapi.StatsResponse\n\n\tstatsReader, err := h.dockerClient.ContainerStats(ctx, h.containerID, mclient.ContainerStatsOptions{\n\t\tIncludePreviousSample: true,\n\t})\n\tif err != nil && err != io.EOF {\n\t\treturn nil, fmt.Errorf(\"failed to collect stats: %w\", err)\n\t}\n\n\t// Ensure the body is not nil to avoid potential panics. The statsReader\n\t// itself cannot be nil, so there is no need to check this.\n\tif statsReader.Body == nil {\n\t\treturn nil, errors.New(\"error decoding stats data: no reader body\")\n\t}\n\n\terr = json.NewDecoder(statsReader.Body).Decode(&stats)\n\t_ = statsReader.Body.Close()\n\tif err != nil && err != io.EOF {\n\t\treturn nil, fmt.Errorf(\"failed to decode Docker response: %w\", err)\n\t}\n\n\tif stats == nil {\n\t\treturn nil, errors.New(\"error decoding stats data: stats were nil\")\n\t}\n\n\treturn stats, nil\n}\n","sourceCodeStart":128,"sourceCodeEnd":161,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/drivers/docker/stats.go#L128-L161","documentation":"collectDockerStats calls Docker's stats API and then decodes the HTTP response body. If the returned statsReader has a nil Body, decoding is impossible, so the driver returns this error instead of panicking. It indicates the Docker API response was malformed or the client returned a response without content.","triggerScenarios":"Docker daemon returning an http.Response whose Body is nil when the driver calls ContainerStats during collectStats for a running task.","commonSituations":"Docker daemon instability or an unexpected API response during `nomad stats` / stats collection; Docker client edge cases on older daemon versions; races where the container is removed while stats are being collected.","solutions":["Check Docker daemon health and version; restart/upgrade the daemon if responses are malformed","Retry the stats collection — the error is usually transient","Verify container is still running at stats time; guard against races by checking task state","Inspect Nomad client logs for repeated failures; if persistent, report to Nomad with Docker version"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// before stats: confirm container is running\nctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\ndefer cancel()\nif _, err := cli.ContainerInspect(ctx, containerID); err != nil { return err }","typeGuard":"func hasBody(r *http.Response) bool { return r != nil && r.Body != nil }","tryCatchPattern":"stats, err := collectDockerStats(ctx, cli, containerID)\nif err != nil {\n    if strings.Contains(err.Error(), \"no reader body\") {\n        // transient: retry with backoff\n        return retry(3, 500*time.Millisecond, func() error { _, err := collectDockerStats(ctx, cli, containerID); return err })\n    }\n    return err\n}","preventionTips":["Keep the Docker daemon upgraded and healthy","Treat stats collection failures as transient and add retries","Check container liveness before requesting stats"],"tags":["nomad","docker","stats","http-response"],"backgroundTag":"empty-response-body","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}