benbjohnson/litestream · error

failed to read response: %w

Error message

failed to read response: %w

What it means

After a successful HTTP GET to the control socket's /info endpoint, Run reads the entire response body with io.ReadAll. If the read fails (connection dropped mid-response, truncated body), the error is wrapped with this message. It indicates a transport-level problem after headers were received, not a server-side logical error.

Source

Thrown at cmd/litestream/info.go:56

	clientTimeout := time.Duration(*timeout) * time.Second
	client := &http.Client{
		Timeout: clientTimeout,
		Transport: &http.Transport{
			DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
				return net.DialTimeout("unix", *socketPath, clientTimeout)
			},
		},
	}

	resp, err := client.Get("http://localhost/info")
	if err != nil {
		return fmt.Errorf("failed to connect to control socket: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("failed to read response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		var errResp litestream.ErrorResponse
		if err := json.Unmarshal(body, &errResp); err == nil && errResp.Error != "" {
			return fmt.Errorf("info failed: %s", errResp.Error)
		}
		return fmt.Errorf("info failed: %s", string(body))
	}

	var result litestream.InfoResponse
	if err := json.Unmarshal(body, &result); err != nil {
		return fmt.Errorf("failed to parse response: %w", err)
	}

	if *jsonOutput {
		output, err := json.MarshalIndent(result, "", "  ")
		if err != nil {

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Simply retry `litestream info`; transient truncation usually resolves once the daemon is stable.
  2. Increase --timeout (e.g. --timeout 60) if the response is being cut off before completion.
  3. Check daemon logs for panics or restarts around the time of the request and fix the underlying crash.
  4. Remove any proxy between the client and the socket that could truncate responses.

Example fix

// before
$ litestream info  # flaky port-forward truncates body
// after
$ kubectl port-forward pod/litestream 8080:8080  # re-establish stable forward
$ litestream info
Defensive patterns

Strategy: retry

Try / catch

resp, err := client.Get("http://localhost/info")
if err == nil {
    body, err := io.ReadAll(resp.Body)
    resp.Body.Close()
    if err != nil {
        // truncated read: transient — retry with backoff and a larger timeout
        return retryWithBackoff(func() error { return runInfo(ctx) }, 3)
    }
}

Prevention

When it happens

Trigger: Daemon closes the connection before sending the full body; network interruption or socket timeout hitting while reading the body; proxy/intermediary truncating the response.

Common situations: Daemon restarting concurrently with the info request; aggressive external timeout killing the socket mid-stream; flaky port-forward in container/Kubernetes setups.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/7f0127e291a09778. Report an issue: GitHub.