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

  1. Guard recovery with a flag: only call Recover once per instance (e.g., if (log.CommittedUntilAddress <= log.BeginAddress) log.Recover();).
  2. If you need a fresh recovery, create a new FasterLog instance over the same device instead of re-calling Recover on the existing one.
  3. 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

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


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)