microsoft/FASTER · error · NotSupportedException

Async operations not supported over protected epoch

Error message

Async operations not supported over protected epoch

What it means

CompletePendingAsync (and other async session APIs) must not run while the current thread holds (protects) the FASTER epoch, because an await can resume on a different thread while the epoch is still held, corrupting epoch-based memory protection. FASTER throws NotSupportedException when ThisInstanceProtected() is true.

Solutions

  1. Do not protect the epoch (call session.UnsafeContext.Dispose()/exit protection) before issuing async operations.
  2. Use the synchronous CompletePending(wait: true) inside epoch-protected code.
  3. Restructure so async completion happens on a thread that never protects the epoch.

Example fix

// before
using (session.UnsafeContext)
{
    await session.CompletePendingAsync(); // throws
}

// after
// exit protection first, then await
await session.CompletePendingAsync();
Defensive patterns

Strategy: type-guard

Validate before calling

if (fht.epoch.ThisInstanceProtected())
    throw new InvalidOperationException("Exit epoch protection before CompletePendingAsync");
await session.CompletePendingAsync();

Type guard

bool EpochUnprotected(ClientSession<K, V, I, O, C, F> session) => !session.FasterKV.epoch.ThisInstanceProtected(); // via store accessor

Try / catch

try
{
    await session.CompletePendingAsync();
}
catch (NotSupportedException ex) when (ex.Message.Contains("protected epoch"))
{
    session.CompletePending(wait: true); // sync fallback inside protected region
}

Prevention

When it happens

Trigger: Calling CompletePendingAsync (or WaitForCommitAsync, which awaits it) from inside code running under session.UnsafeContext or within a lockable/protected epoch region, e.g. inside an IFunctions callback or inside (ctx.Functions) epoch protection blocks.

Common situations: Mixing sync epoch-protected code with async APIs: awaiting inside a 'using (session.UnsafeContext)' style protected region, or issuing async calls from within RMW/Read callbacks.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of microsoft/FASTER@321d872eab (2026-09-15). Data as JSON: /api/errors/36858436cceec501. Report an issue: GitHub.

Appendix: source

Thrown at cs/src/core/ClientSession/ClientSession.cs:887

        /// <inheritdoc/>
        public ValueTask CompletePendingAsync(bool waitForCommit = false, CancellationToken token = default)
            => CompletePendingAsync(false, waitForCommit, token);

        /// <inheritdoc/>
        public async ValueTask<CompletedOutputIterator<Key, Value, Input, Output, Context>> CompletePendingWithOutputsAsync(bool waitForCommit = false, CancellationToken token = default)
        {
            InitializeCompletedOutputs();
            await CompletePendingAsync(true, waitForCommit, token).ConfigureAwait(false);
            return this.completedOutputs;
        }

        private async ValueTask CompletePendingAsync(bool getOutputs, bool waitForCommit = false, CancellationToken token = default)
        {
            token.ThrowIfCancellationRequested();

            if (fht.epoch.ThisInstanceProtected())
                throw new NotSupportedException("Async operations not supported over protected epoch");

            // Complete all pending operations on session
            await fht.CompletePendingAsync(this.FasterSession, token, getOutputs ? this.completedOutputs : null).ConfigureAwait(false);

            // Wait for commit if necessary
            if (waitForCommit)
                await WaitForCommitAsync(token).ConfigureAwait(false);
        }

        /// <summary>
        /// Check if at least one synchronous request is ready for CompletePending to be called on
        /// Returns completed immediately if there are no outstanding synchronous requests
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public async ValueTask ReadyToCompletePendingAsync(CancellationToken token = default)
        {
            token.ThrowIfCancellationRequested();

View on GitHub (pinned to 321d872eab)