hashicorp/nomad · error

error decoding stats data: no reader body

Error message

error decoding stats data: no reader body

What it means

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.

Source

Thrown at drivers/docker/stats.go:146

}

// collectDockerStats performs the stats collection from the Docker API. It is
// split into its own function for the purpose of aiding testing.
func (h *taskHandle) collectDockerStats(ctx context.Context) (*containerapi.StatsResponse, error) {

	var stats *containerapi.StatsResponse

	statsReader, err := h.dockerClient.ContainerStats(ctx, h.containerID, mclient.ContainerStatsOptions{
		IncludePreviousSample: true,
	})
	if err != nil && err != io.EOF {
		return nil, fmt.Errorf("failed to collect stats: %w", err)
	}

	// Ensure the body is not nil to avoid potential panics. The statsReader
	// itself cannot be nil, so there is no need to check this.
	if statsReader.Body == nil {
		return nil, errors.New("error decoding stats data: no reader body")
	}

	err = json.NewDecoder(statsReader.Body).Decode(&stats)
	_ = statsReader.Body.Close()
	if err != nil && err != io.EOF {
		return nil, fmt.Errorf("failed to decode Docker response: %w", err)
	}

	if stats == nil {
		return nil, errors.New("error decoding stats data: stats were nil")
	}

	return stats, nil
}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Check Docker daemon health and version; restart/upgrade the daemon if responses are malformed
  2. Retry the stats collection — the error is usually transient
  3. Verify container is still running at stats time; guard against races by checking task state
  4. Inspect Nomad client logs for repeated failures; if persistent, report to Nomad with Docker version
Defensive patterns

Strategy: retry

Validate before calling

// before stats: confirm container is running
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if _, err := cli.ContainerInspect(ctx, containerID); err != nil { return err }

Type guard

func hasBody(r *http.Response) bool { return r != nil && r.Body != nil }

Try / catch

stats, err := collectDockerStats(ctx, cli, containerID)
if err != nil {
    if strings.Contains(err.Error(), "no reader body") {
        // transient: retry with backoff
        return retry(3, 500*time.Millisecond, func() error { _, err := collectDockerStats(ctx, cli, containerID); return err })
    }
    return err
}

Prevention

When it happens

Trigger: Docker daemon returning an http.Response whose Body is nil when the driver calls ContainerStats during collectStats for a running task.

Common situations: 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.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/1f9f3a18b30338fc. Report an issue: GitHub.