benbjohnson/litestream · error

failed to marshal request: %w

Error message

failed to marshal request: %w

What it means

`litestream start` builds a litestream.StartRequest{Path, Timeout} and serializes it with json.Marshal before POSTing to the control socket at http://localhost/start. If marshaling fails, Run returns 'failed to marshal request' wrapping the json error. In practice this is nearly impossible for this fixed struct (all fields are JSON-encodable), so it almost always indicates an environment/runtime-level problem rather than bad user input.

Source

Thrown at cmd/litestream/start.go:60

	// Create HTTP client that connects via Unix socket with timeout
	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)
			},
		},
	}

	req := litestream.StartRequest{
		Path:    dbPath,
		Timeout: *timeout,
	}
	reqBody, err := json.Marshal(req)
	if err != nil {
		return fmt.Errorf("failed to marshal request: %w", err)
	}

	resp, err := client.Post("http://localhost/start", "application/json", bytes.NewReader(reqBody))
	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("start failed: %s", errResp.Error)
		}

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Retry the command — the failure is transient if caused by memory pressure
  2. Free system memory / check dmesg for OOM killer activity if it recurs
  3. If it persists, rebuild litestream (`go build -o bin/litestream ./cmd/litestream`) to rule out a corrupted binary
  4. Report to litestream issues if reproducible, including the wrapped json error text
Defensive patterns

Strategy: try-catch

Try / catch

// CLI wrapper: distinguish usage errors from transient failures
if err := litestreamStart(dbPath, timeout); err != nil {
	if strings.Contains(err.Error(), "failed to marshal request") {
		// transient/runtime-level: retry once, then report with wrapped cause
		if retryErr := litestreamStart(dbPath, timeout); retryErr != nil {
			return fmt.Errorf("start failed after retry: %w", retryErr)
		}
		return nil
	}
	return err
}

Prevention

When it happens

Trigger: json.Marshal returning an error for the StartRequest value. With the fixed struct (string Path, int Timeout) this only occurs under exotic runtime conditions such as memory allocation failure; it cannot be triggered by user-supplied values since both fields are plain encodable types.

Common situations: Extremely rare — encountered only during system-level resource exhaustion (OOM conditions) or a corrupted Go runtime; not caused by configuration or user input.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


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