{"record":{"id":"40ef32126b3f81ee","repo":"benbjohnson/litestream","slug":"cannot-specify-both-txid-timestamp-to-restore","errorCode":null,"errorMessage":"cannot specify both TXID & timestamp to restore","messagePattern":"cannot specify both TXID & timestamp to restore","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"replica.go","lineNumber":1516,"sourceCode":"\t// Find snapshots at the snapshot level that are before the timestamp.\n\tsnapshots, err := FindLTXFiles(ctx, r.Client, SnapshotLevel, true, func(info *ltx.FileInfo) (bool, error) {\n\t\treturn info.CreatedAt.Before(timestamp), nil\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"find LTX snapshots: %w\", err)\n\t}\n\tif len(snapshots) == 0 {\n\t\treturn nil, nil\n\t}\n\n\t// Return the latest snapshot before the timestamp (last in the sorted list).\n\treturn snapshots[len(snapshots)-1], nil\n}\n\n// CalcRestorePlan returns a list of storage paths to restore a snapshot at the given TXID.\nfunc CalcRestorePlan(ctx context.Context, client ReplicaClient, txID ltx.TXID, timestamp time.Time, logger *slog.Logger) ([]*ltx.FileInfo, error) {\n\tif txID != 0 && !timestamp.IsZero() {\n\t\treturn nil, fmt.Errorf(\"cannot specify both TXID & timestamp to restore\")\n\t}\n\n\tvar infos ltx.FileInfoSlice\n\tlogger = logger.With(\"target\", txID)\n\n\t// Start with latest snapshot before target TXID or timestamp.\n\t// Pass useMetadata flag to enable accurate timestamp fetching for timestamp-based restore.\n\tvar snapshot *ltx.FileInfo\n\tsnapshotItr, err := client.LTXFiles(ctx, SnapshotLevel, 0, !timestamp.IsZero())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor snapshotItr.Next() {\n\t\tinfo := snapshotItr.Item()\n\t\tlogger.Debug(\"finding snapshot before target TXID or timestamp\", \"snapshot\", info.MaxTXID)\n\t\tif txID != 0 && info.MaxTXID > txID {\n\t\t\tcontinue\n\t\t}","sourceCodeStart":1498,"sourceCodeEnd":1534,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/replica.go#L1498-L1534","documentation":"CalcRestorePlan rejects a restore request that supplies both a TXID and a timestamp, because the restore target is ambiguous. Exactly one of txID or timestamp must be nonzero/zero respectively. The public API surfaces this as a plain error with no wrapped cause.","triggerScenarios":"Calling Restore/CalcRestorePlan (or triggering SetTargetTime/ResetTime/waitForRestorePlan paths) with both txID != 0 and timestamp set, e.g. via library API CalcRestorePlan(ctx, client, txid, ts, logger) or CLI flags combining -txid and -timestamp.","commonSituations":"Library users computing a plan for a known TXID while leaving a default timestamp from config; scripts building restore args that always append -timestamp; restoring to a specific transaction while a stale target time is persisted.","solutions":["Pass only one target: set timestamp to the zero value time.Time{} when restoring by TXID.","If using the CLI, drop either -txid or -timestamp.","Clear a stale persisted target time (SetTargetTime/ResetTime state) before issuing a TXID restore."],"exampleFix":"// before\nplan, err := CalcRestorePlan(ctx, client, txID, targetTime, logger)\n// after\nvar zeroTime time.Time\nplan, err := CalcRestorePlan(ctx, client, txID, zeroTime, logger)","handlingStrategy":"validation","validationCode":"// Go: enforce mutual exclusivity before calling the API\nif txID != 0 && !timestamp.IsZero() {\n    return fmt.Errorf(\"pass either txID or timestamp, not both\")\n}\nplan, err := ltx.CalcRestorePlan(ctx, client, txID, timestamp, logger)","typeGuard":null,"tryCatchPattern":"if err != nil {\n    if err.Error() == \"cannot specify both TXID & timestamp to restore\" {\n        // clear one of the targets and rebuild the plan\n    }\n    return err\n}","preventionTips":["Use the zero value time.Time{} explicitly when restoring by TXID.","In CLIs/scripts, build flags so -txid and -timestamp are mutually exclusive.","Reset stale target-time state (ResetTime) before TXID-based restores."],"tags":["restore","argument-validation","pitr","txid"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}