benbjohnson/litestream · error
failed to format response: %w
Error message
failed to format response: %w
What it means
When --dry-run succeeds, restore can emit the plan as JSON via json.MarshalIndent. If marshaling the plan structure fails (unexpectedly, since the plan is composed of marshalable types), the error is wrapped as "failed to format response". This is a defensive serialization guard on the CLI output path, not a restore failure — the plan itself was computed successfully.
Source
Thrown at cmd/litestream/restore.go:128
if r, err = c.loadFromConfig(ctx, fs.Arg(0), *configPath, !*noExpandEnv, *ifDBNotExists, &opt); errors.Is(err, errSkipDBExists) {
slog.Info("database already exists, skipping")
return nil
} else if err != nil {
return err
}
}
if *dryRun {
plan, err := c.dryRunPlan(ctx, fs.Arg(0), r, opt)
if errors.Is(err, litestream.ErrTxNotAvailable) {
return fmt.Errorf("no matching backup files available")
} else if err != nil {
return err
}
if *jsonOutput {
output, err := json.MarshalIndent(plan, "", " ")
if err != nil {
return fmt.Errorf("failed to format response: %w", err)
}
fmt.Println(string(output))
return nil
}
c.printDryRunPlan(plan)
return nil
}
if !opt.Follow {
if err := c.prepareOutputPath(opt.OutputPath, *force); err != nil {
return err
}
}
txid := c.restoreTXID(ctx, r, opt)
start := time.Now()
if err := r.Restore(ctx, opt); errors.Is(err, litestream.ErrTxNotAvailable) {
if *ifReplicaExists {View on GitHub (pinned to 4ed7a308f6)
Solutions
- Re-run with the non-JSON dry-run output (omit --json) to still see the plan via c.printDryRunPlan
- Check the wrapped inner error — it identifies the offending type/value
- If running a patched build, ensure all fields added to the plan struct are JSON-serializable
- Report upstream if it reproduces on a vanilla build
Example fix
// before (fork added non-serializable field)
type plan struct { Meta meta; Ch chan int `json:"-"` }
// after
type plan struct { Meta meta } Defensive patterns
Strategy: try-catch
Try / catch
output, err := json.MarshalIndent(plan, "", " ")
if err != nil {
// fall back to human-readable output instead of failing
printDryRunPlan(plan)
return nil
} Prevention
- Keep dry-run plan structs JSON-serializable (no func/chan fields)
- If forking the CLI, add round-trip JSON tests for output structs
- Fall back to text output when JSON formatting fails
When it happens
Trigger: `litestream restore --dry-run --json ...` where json.MarshalIndent(plan) returns an error — in practice only from an unsupported value type slipping into the plan struct (e.g. channel/func field) or a custom MarshalJSON returning an error.
Common situations: Very rare; typically encountered after local modifications/forks of the CLI that add non-serializable fields to the dry-run plan, or custom JSON marshaling bugs.
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
- failed to format response: %w
- failed to format response: %w
- failed to marshal request: %w
- failed to format response: %w
- dry run failed: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/af9fb574945b87f2.
Report an issue: GitHub.