microsoft/FASTER · error · FasterException

Cannot use CompleteCheckpointAsync when using non-async…

Error message

Cannot use CompleteCheckpointAsync when using non-async sessions

What it means

CompleteCheckpointAsync must run inside an async (epoch-unprotected) context. If the calling thread currently holds the epoch protection (ThisInstanceProtected() is true), it means the caller is using a synchronous/non-async session and blocking-epoch semantics, so the async wait would deadlock or violate epoch discipline; the library throws instead.

Solutions

  1. Use the async checkpoint APIs end-to-end: TakeFullCheckpointAsync/CompleteCheckpointAsync from a non-epoch-protected context
  2. If using blocking checkpoints, rely on their own completion semantics instead of calling CompleteCheckpointAsync
  3. Move the CompleteCheckpointAsync call out of epoch-protected regions and session callbacks onto an independent async continuation

Example fix

// before: same thread still epoch-protected
fasterKV.TakeFullCheckpoint(out token); // blocking, protected
await fasterKV.CompleteCheckpointAsync(); // throws
// after
(long token, _) = await fasterKV.TakeFullCheckpointAsync(); // async path handles completion
Defensive patterns

Strategy: try-catch

Validate before calling

// Only call CompleteCheckpointAsync if the current thread is NOT epoch-protected
if (fasterKV is not null /* and caller is on async session flow, not inside callbacks */)
{
    // proceed with async completion only from async session paths
}

Try / catch

try { await fasterKV.CompleteCheckpointAsync(); }
catch (FasterException ex) when (ex.Message.Contains("CompleteCheckpointAsync when using non-async sessions"))
{
    // you are on a blocking/protected path: switch to the async checkpoint API
}

Prevention

When it happens

Trigger: Calling CompleteCheckpointAsync from code that has entered epoch protection (e.g., inside a session's synchronous callback, insideEpochProtected region, or after manually calling epoch.Resume()/Protect on the same thread), instead of completing the checkpoint on a separate async flow.

Common situations: Mixing blocking (synchronous) session APIs like TakeFullCheckpoint followed by CompleteCheckpointAsync on the same protected thread; calling CompleteCheckpointAsync inside IFasterStateMachine callbacks; wrappers that call async APIs while holding epoch protection.

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/050aff2a8059cd08. Report an issue: GitHub.

Appendix: source

Thrown at cs/src/core/Index/FASTER/FASTER.cs:542

        /// Asynchronously recover from specific index and log token (blocking operation)
        /// </summary>
        /// <param name="indexCheckpointToken"></param>
        /// <param name="hybridLogCheckpointToken"></param>
        /// <param name="numPagesToPreload">Number of pages to preload into memory after recovery</param>
        /// <param name="undoNextVersion">Whether records with versions beyond checkpoint version need to be undone (and invalidated on log)</param>
        /// <param name="cancellationToken">Cancellation token</param>
        /// <returns>Version we actually recovered to</returns>
        public ValueTask<long> RecoverAsync(Guid indexCheckpointToken, Guid hybridLogCheckpointToken, int numPagesToPreload = -1, bool undoNextVersion = true, CancellationToken cancellationToken = default) 
            => InternalRecoverAsync(indexCheckpointToken, hybridLogCheckpointToken, numPagesToPreload, undoNextVersion, -1, cancellationToken);

        /// <summary>
        /// Wait for ongoing checkpoint to complete
        /// </summary>
        /// <returns></returns>
        public async ValueTask CompleteCheckpointAsync(CancellationToken token = default)
        {
            if (epoch.ThisInstanceProtected())
                throw new FasterException("Cannot use CompleteCheckpointAsync when using non-async sessions");

            token.ThrowIfCancellationRequested();

            while (true)
            {
                var systemState = this.systemState;
                if (systemState.Phase == Phase.REST || systemState.Phase == Phase.PREPARE_GROW ||
                    systemState.Phase == Phase.IN_PROGRESS_GROW)
                    return;

                List<ValueTask> valueTasks = new();

                try
                {
                    epoch.Resume();
                    ThreadStateMachineStep<Empty, Empty, Empty, NullFasterSession>(null, NullFasterSession.Instance, valueTasks, token);
                }
                catch (Exception)

View on GitHub (pinned to 321d872eab)