{"record":{"id":"102ad1c3004dc117","repo":"benbjohnson/litestream","slug":"target-time-required","errorCode":null,"errorMessage":"target time required","messagePattern":"target time required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":1253,"sourceCode":"\t\tf.syncStop = make(chan struct{})\n\t\tstopCh := f.syncStop\n\t\ttickerCh := f.syncTicker.C\n\t\tf.wg.Add(1)\n\t\tgo func() { defer f.wg.Done(); f.syncLoop(stopCh, tickerCh) }()\n\t}\n\n\t// Start compaction monitors if enabled\n\tif f.compactor != nil && f.vfs != nil {\n\t\tf.startCompactionMonitors()\n\t}\n\n\treturn nil\n}\n\n// SetTargetTime rebuilds the page index to view the database at a specific time.\nfunc (f *VFSFile) SetTargetTime(ctx context.Context, timestamp time.Time) error {\n\tif timestamp.IsZero() {\n\t\treturn fmt.Errorf(\"target time required\")\n\t}\n\n\tinfos, err := CalcRestorePlan(ctx, f.client, 0, timestamp, f.logger)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot calc restore plan: %w\", err)\n\t} else if len(infos) == 0 {\n\t\treturn fmt.Errorf(\"no backup files available\")\n\t}\n\n\t// Disable hydrated reads during time travel - hydrated file is at latest state\n\tif f.hydrator != nil && f.hydrator.Complete() {\n\t\tf.hydrator.Disable()\n\t\tf.logger.Debug(\"hydration disabled for time travel\", \"target\", timestamp)\n\t}\n\n\treturn f.rebuildIndex(ctx, infos, &timestamp)\n}\n","sourceCodeStart":1235,"sourceCodeEnd":1271,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L1235-L1271","documentation":"Returned by VFSFile.SetTargetTime (time-travel read view) when the caller passes a zero time.Time. The method rebuilds the page index at a specific point in time; a zero timestamp is meaningless for CalcRestorePlan, so it is rejected up front with this sentinel error.","triggerScenarios":"Calling SetTargetTime(ctx, time.Time{}) — e.g. an unset struct field, a failed JSON/DB decode of the target time, or a var declared without initialization — instead of a real restore timestamp.","commonSituations":"App config where the restore-time option is optional and left empty; deserialization of a missing timestamp field defaulting to the zero value; passing time.Now() result stored in a zero-valued variable due to an earlier error.","solutions":["Check timestamp.IsZero() before calling SetTargetTime and skip/branch to ResetTime when unset","Parse the user-supplied time with error handling so a parse failure doesn't silently yield a zero time","Use ResetTime(ctx) when you actually want the latest state instead of a target time"],"exampleFix":"// before\nvar target time.Time\nfile.SetTargetTime(ctx, target) // panics-free but errors\n// after\nif target.IsZero() {\n    err = file.ResetTime(ctx)\n} else {\n    err = file.SetTargetTime(ctx, target)\n}","handlingStrategy":"validation","validationCode":"if targetTime.IsZero() { return errors.New(\"target time required before SetTargetTime\") }","typeGuard":"func hasTarget(t time.Time) bool { return !t.IsZero() }","tryCatchPattern":"if err := file.SetTargetTime(ctx, t); err != nil {\n    if err.Error() == \"target time required\" {\n        return file.ResetTime(ctx) // fall back to latest\n    }\n    return err\n}","preventionTips":["Check timestamps parsed from user input/JSON for zero values","Use pointers or explicit 'set' flags for optional time config","Prefer ResetTime when latest state is intended"],"tags":["time-travel","argument-validation","timestamp"],"backgroundTag":"missing-required-argument","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"}