benbjohnson/litestream · error

failed to parse response: %w

Error message

failed to parse response: %w

What it means

The daemon returned HTTP 200, but the response body could not be unmarshaled into litestream.StartResponse. The CLI wraps the json.Unmarshal error as 'failed to parse response'. This is a contract mismatch between daemon and CLI: truncated, non-JSON, or schema-drifted JSON.

Source

Thrown at cmd/litestream/start.go:84

	}
	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("start failed: %s", errResp.Error)
		}
		return fmt.Errorf("start failed: %s", string(body))
	}

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

	confirmation := StartStopResult{
		Status: result.Status,
		DBPath: result.Path,
		State:  "running",
		TXID:   result.TXID,
		Socket: *socketPath,
	}
	if err := printStartStopResult(confirmation, *jsonOutput); err != nil {
		return err
	}

	return nil
}

type StartStopResult struct {
	Status string `json:"status"`

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Pin CLI and daemon to the same litestream version
  2. Fetch the raw body (curl --unix-socket /var/run/litestream.sock http://localhost/start) to inspect what the daemon actually returned
  3. Retry in case of a truncated response; check daemon logs for panics mid-write
  4. If persistent, file/inspect for a daemon bug writing malformed JSON on success
Defensive patterns

Strategy: type-guard

Validate before calling

// verify compatibility before parsing in wrappers:
// compare `litestream version` output of daemon and CLI

Type guard

func validStartResponse(b []byte) bool {
    var r litestream.StartResponse
    return json.Unmarshal(b, &r) == nil && r.Status != ""
}

Try / catch

var jsonErr *json.UnmarshalTypeError
if errors.As(err, &jsonErr) {
    log.Printf("daemon/CLI schema mismatch at %s", jsonErr.Field)
}

Prevention

When it happens

Trigger: Successful /start response whose body is truncated (connection cut after headers), is not JSON (proxy injected content), or whose fields don't match StartResponse because CLI and daemon versions differ.

Common situations: Upgrading the CLI but not the daemon (or vice versa) so response fields changed; a caching/proxy layer rewriting the body; daemon bug emitting partial JSON on success.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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