microsoft/FASTER · error · FasterException
Already recovered until address
Error message
Already recovered until address {CommittedUntilAddress} What it means
FasterLog.Recover rehydrates the log's commit table from disk and may only be run on a log instance that has not yet been recovered. If CommittedUntilAddress is already past BeginAddress, recovery state is already loaded and a second Recover call is rejected with FasterException. This prevents clobbering the committed-view established by an earlier recovery or subsequent commits.
Solutions
- Guard recovery with a flag: only call Recover once per instance (e.g., if (log.CommittedUntilAddress <= log.BeginAddress) log.Recover();).
- If you need a fresh recovery, create a new FasterLog instance over the same device instead of re-calling Recover on the existing one.
- Coordinate recovery with a lock/once-initializer so only one thread performs it.
Example fix
// before
log.Recover(-1);
log.Recover(commitNum); // throws: already recovered
// after
if (log.CommittedUntilAddress <= log.BeginAddress)
log.Recover(commitNum); Defensive patterns
Strategy: validation
Validate before calling
void RecoverOnce(FasterLog log, int commitNum = -1)
{
if (log.CommittedUntilAddress <= log.BeginAddress)
log.Recover(commitNum);
} Try / catch
try { log.Recover(commitNum); }
catch (FasterException ex) when (ex.Message.StartsWith("Already recovered until address"))
{
// already recovered: treat as success / idempotent
} Prevention
- Make recovery a one-time, synchronized step (lock or Lazy) per log instance.
- Never retry Recover after partial failure; construct a fresh FasterLog instead.
- Check CommittedUntilAddress > BeginAddress before recovering.
When it happens
Trigger: Calling log.Recover() (optionally with a requestedCommitNum) on a FasterLog instance that was already recovered - typically a second call on the same object, or Recover after the instance was constructed with recovery settings that already restored state.
Common situations: Retry logic that re-invokes Recover after a failure; calling Recover after another thread already recovered; mixing recovery done in the constructor (Recover) with an explicit Recover call; reusing a cached FasterLog instance across service restarts within the process.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- This method can only be used with a read-only FasterLog…
- Cannot commit in read-only mode
- Uninitialized page found during scan at page
- Unable to recover from previous commit. Inner exception:
- Invalid version found during commit recovery
AI-assisted analysis of microsoft/FASTER@321d872eab (2026-09-15).
Data as JSON: /api/errors/5dc3756beed1c04c.
Report an issue: GitHub.
Appendix: source
Thrown at cs/src/core/FasterLog/FasterLog.cs:282
CommittedUntilAddress = committedUntilAddress;
CommittedBeginAddress = beginAddress;
SafeTailAddress = committedUntilAddress;
commitNum = lastCommitNum;
this.beginAddress = beginAddress;
if (lastCommitNum > 0) logCommitManager.OnRecovery(lastCommitNum);
}
/// <summary>
/// Recover FasterLog to the specific commit number, or latest if -1
/// </summary>
/// <param name="requestedCommitNum">Requested commit number</param>
public void Recover(long requestedCommitNum = -1)
{
if (CommittedUntilAddress > BeginAddress)
throw new FasterException($"Already recovered until address {CommittedUntilAddress}");
Dictionary<string, long> it;
if (requestedCommitNum == -1)
RestoreLatest(out it, out RecoveredCookie);
else
RestoreSpecificCommit(requestedCommitNum, out it, out RecoveredCookie);
RecoveredIterators = it;
}
/// <summary>
/// Create new log instance asynchronously
/// </summary>
/// <param name="logSettings"></param>
/// <param name="cancellationToken"></param>
public static async ValueTask<FasterLog> CreateAsync(FasterLogSettings logSettings, CancellationToken cancellationToken = default)
{
var fasterLog = new FasterLog(logSettings, false);
if (logSettings.TryRecoverLatest)View on GitHub (pinned to 321d872eab)