{"record":{"id":"5dc3756beed1c04c","repo":"microsoft/FASTER","slug":"already-recovered-until-address-committeduntiladdress","errorCode":null,"errorMessage":"Already recovered until address {CommittedUntilAddress}","messagePattern":"Already recovered until address (.+?)","errorType":"exception","errorClass":"FasterException","httpStatus":null,"severity":"error","filePath":"cs/src/core/FasterLog/FasterLog.cs","lineNumber":282,"sourceCode":"\n            CommittedUntilAddress = committedUntilAddress;\n            CommittedBeginAddress = beginAddress;\n            SafeTailAddress = committedUntilAddress;\n\n            commitNum = lastCommitNum;\n            this.beginAddress = beginAddress;\n\n            if (lastCommitNum > 0) logCommitManager.OnRecovery(lastCommitNum);\n        }\n\n        /// <summary>\n        /// Recover FasterLog to the specific commit number, or latest if -1\n        /// </summary>\n        /// <param name=\"requestedCommitNum\">Requested commit number</param>\n        public void Recover(long requestedCommitNum = -1)\n        {\n            if (CommittedUntilAddress > BeginAddress)\n                throw new FasterException($\"Already recovered until address {CommittedUntilAddress}\");\n\n            Dictionary<string, long> it;\n            if (requestedCommitNum == -1)\n                RestoreLatest(out it, out RecoveredCookie);\n            else\n                RestoreSpecificCommit(requestedCommitNum, out it, out RecoveredCookie);\n            RecoveredIterators = it;\n        }\n\n        /// <summary>\n        /// Create new log instance asynchronously\n        /// </summary>\n        /// <param name=\"logSettings\"></param>\n        /// <param name=\"cancellationToken\"></param>\n        public static async ValueTask<FasterLog> CreateAsync(FasterLogSettings logSettings, CancellationToken cancellationToken = default)\n        {\n            var fasterLog = new FasterLog(logSettings, false);\n            if (logSettings.TryRecoverLatest)","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/microsoft/FASTER/blob/321d872eabda6a0345c8bd76419f89723ed864ae/cs/src/core/FasterLog/FasterLog.cs#L264-L300","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlog.Recover(-1);\nlog.Recover(commitNum); // throws: already recovered\n// after\nif (log.CommittedUntilAddress <= log.BeginAddress)\n    log.Recover(commitNum);","handlingStrategy":"validation","validationCode":"void RecoverOnce(FasterLog log, int commitNum = -1)\n{\n    if (log.CommittedUntilAddress <= log.BeginAddress)\n        log.Recover(commitNum);\n}","typeGuard":null,"tryCatchPattern":"try { log.Recover(commitNum); }\ncatch (FasterException ex) when (ex.Message.StartsWith(\"Already recovered until address\"))\n{\n    // already recovered: treat as success / idempotent\n}","preventionTips":["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."],"tags":["fasterlog","recovery","invalid-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"321d872eabda6a0345c8bd76419f89723ed864ae","analyzedAt":"2026-09-15T22:18:00.693Z","contentChangedAt":"2026-09-15T22:18:00.693Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}