{"record":{"id":"af9fb574945b87f2","repo":"benbjohnson/litestream","slug":"failed-to-format-response-w-af9fb5","errorCode":null,"errorMessage":"failed to format response: %w","messagePattern":"failed to format response: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/litestream/restore.go","lineNumber":128,"sourceCode":"\t\tif r, err = c.loadFromConfig(ctx, fs.Arg(0), *configPath, !*noExpandEnv, *ifDBNotExists, &opt); errors.Is(err, errSkipDBExists) {\n\t\t\tslog.Info(\"database already exists, skipping\")\n\t\t\treturn nil\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif *dryRun {\n\t\tplan, err := c.dryRunPlan(ctx, fs.Arg(0), r, opt)\n\t\tif errors.Is(err, litestream.ErrTxNotAvailable) {\n\t\t\treturn fmt.Errorf(\"no matching backup files available\")\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif *jsonOutput {\n\t\t\toutput, err := json.MarshalIndent(plan, \"\", \"  \")\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to format response: %w\", err)\n\t\t\t}\n\t\t\tfmt.Println(string(output))\n\t\t\treturn nil\n\t\t}\n\t\tc.printDryRunPlan(plan)\n\t\treturn nil\n\t}\n\n\tif !opt.Follow {\n\t\tif err := c.prepareOutputPath(opt.OutputPath, *force); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\ttxid := c.restoreTXID(ctx, r, opt)\n\tstart := time.Now()\n\tif err := r.Restore(ctx, opt); errors.Is(err, litestream.ErrTxNotAvailable) {\n\t\tif *ifReplicaExists {","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/cmd/litestream/restore.go#L110-L146","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","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"],"exampleFix":"// before (fork added non-serializable field)\ntype plan struct { Meta meta; Ch chan int `json:\"-\"` }\n// after\ntype plan struct { Meta meta }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"output, err := json.MarshalIndent(plan, \"\", \"  \")\nif err != nil {\n    // fall back to human-readable output instead of failing\n    printDryRunPlan(plan)\n    return nil\n}","preventionTips":["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"],"tags":["cli","json","serialization","dry-run"],"backgroundTag":"json-marshal-failed","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}