{"record":{"id":"a16ad85cfe35097b","repo":"benbjohnson/litestream","slug":"no-ltx-files-for-hydration","errorCode":null,"errorMessage":"no ltx files for hydration","messagePattern":"no ltx files for hydration","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":782,"sourceCode":"\tdefer func() {\n\t\tfor _, rd := range rdrs {\n\t\t\tif closer, ok := rd.(io.Closer); ok {\n\t\t\t\t_ = closer.Close()\n\t\t\t}\n\t\t}\n\t}()\n\n\tfor _, info := range infos {\n\t\th.logger.Debug(\"opening ltx file for hydration\", \"level\", info.Level, \"min\", info.MinTXID, \"max\", info.MaxTXID)\n\t\trc, err := h.client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, 0)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"open ltx file: %w\", err)\n\t\t}\n\t\trdrs = append(rdrs, rc)\n\t}\n\n\tif len(rdrs) == 0 {\n\t\treturn fmt.Errorf(\"no ltx files for hydration\")\n\t}\n\n\t// Compact and decode using io.Pipe pattern\n\tpr, pw := io.Pipe()\n\tc, err := ltx.NewCompactor(pw, rdrs)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new ltx compactor: %w\", err)\n\t}\n\tc.HeaderFlags = ltx.HeaderFlagNoChecksum\n\th.compactor = c\n\n\tgo func() {\n\t\t_ = pw.CloseWithError(c.Compact(ctx))\n\t}()\n\n\th.mu.Lock()\n\tdefer h.mu.Unlock()\n","sourceCodeStart":764,"sourceCodeEnd":800,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L764-L800","documentation":"Hydrator.Restore refuses to run when, after iterating the provided FileInfo list, zero readers were opened (empty `infos` yields no LTX readers). Restoring a database from nothing would produce an empty/corrupt hydration file, so the hydrator fails fast with this sentinel-style error.","triggerScenarios":"Calling Hydrator.Restore(ctx, nil) or Restore with an empty []*ltx.FileInfo slice — e.g. the caller listed LTX files at a TXID beyond everything the replica retains, or the replica bucket is empty.","commonSituations":"Fresh database whose first snapshot has not replicated yet; restore-from offset newer than the latest replica TXID; bucket pointed at the wrong path/prefix so listing returns nothing; retention wiped all files.","solutions":["Check that the replica actually contains LTX files (`litestream ltx -level all` or ListLTXFiles) and that the configured path/prefix matches the database.","Re-run restore with infos covering a valid TXID range (start from 1 / the oldest retained snapshot) instead of an offset past the newest file.","If the replica is genuinely empty, trigger a new snapshot/backup before attempting hydration.","Guard callers: only invoke Restore when len(infos) > 0 and fall back to a full replication sync otherwise."],"exampleFix":"// before\nif err := hydrator.Restore(ctx, infos); err != nil { return err }\n// after\nif len(infos) == 0 {\n    return fmt.Errorf(\"no ltx files on replica; run a snapshot first\")\n}\nif err := hydrator.Restore(ctx, infos); err != nil { return err }","handlingStrategy":"validation","validationCode":"if len(infos) == 0 {\n    return fmt.Errorf(\"no ltx files available; trigger a snapshot before restore\")\n}","typeGuard":null,"tryCatchPattern":"if err := hydrator.Restore(ctx, infos); err != nil {\n    if strings.Contains(err.Error(), \"no ltx files\") {\n        // fall back to forcing a fresh replication snapshot\n    }\n}","preventionTips":["Always guard Restore with len(infos) > 0","Confirm the replica path/prefix with `litestream ltx` before hydrating","Ensure an initial snapshot has replicated before serving reads"],"tags":["replication","empty-input","restore"],"backgroundTag":"empty-result-set","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"}