microsoft/garnet · error · TsavoriteException

Transactional method call when BeginTransactional has not be

Error message

Transactional method call when BeginTransactional has not been called

What it means

CheckIsAcquiredTransactional verifies that the session has entered a transactional scope (via BeginTransactional) before any transactional operation is allowed. If isAcquiredTransactional is false and a transactional method (LocksAcquired, ReleaseTransactional, etc.) is called, it throws. This catches transaction API misuse early.

Source

Thrown at libs/storage/Tsavorite/cs/src/core/ClientSession/ClientSession.cs:79

            CheckIsAcquiredTransactional(sessionFunctions);
            sessionFunctions.Ctx.txnVersion = txnVersion;
        }

        internal void ReleaseTransactional<TSessionFunctions>(TSessionFunctions sessionFunctions)
            where TSessionFunctions : ISessionFunctionsWrapper<TInput, TOutput, TContext, TStoreFunctions, TAllocator>
        {
            CheckIsAcquiredTransactional(sessionFunctions);
            if (TotalLockCount > 0)
                throw new TsavoriteException($"EndTransactional called with locks held: {sharedLockCount} shared locks, {exclusiveLockCount} exclusive locks");
            sessionFunctions.Ctx.isAcquiredTransactional = false;
            sessionFunctions.Ctx.txnVersion = 0;
        }

        internal void CheckIsAcquiredTransactional<TSessionFunctions>(TSessionFunctions sessionFunctions)
            where TSessionFunctions : ISessionFunctionsWrapper<TInput, TOutput, TContext, TStoreFunctions, TAllocator>
        {
            if (!sessionFunctions.Ctx.isAcquiredTransactional)
                throw new TsavoriteException("Transactional method call when BeginTransactional has not been called");
        }

        void CheckIsNotAcquiredTransactional<TSessionFunctions>(TSessionFunctions sessionFunctions)
            where TSessionFunctions : ISessionFunctionsWrapper<TInput, TOutput, TContext, TStoreFunctions, TAllocator>
        {
            if (sessionFunctions.Ctx.isAcquiredTransactional)
                throw new TsavoriteException("BeginTransactional cannot be called twice (call EndTransactional first)");
        }

        internal ClientSession(
            TsavoriteKV<TStoreFunctions, TAllocator> store,
            TsavoriteKV<TStoreFunctions, TAllocator>.TsavoriteExecutionContext<TInput, TOutput, TContext> ctx,
            TFunctions functions,
            bool enableConsistentRead = false,
            ILoggerFactory loggerFactory = null)
        {
            if (enableConsistentRead)
            {

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Always call session.BeginTransactional() before issuing transactional lock/read/write operations.
  2. Wrap transaction usage in try/finally with EndTransactional in finally to ensure balanced pairing.
  3. Add a debug assertion or guard that checks the transactional state before calling transactional methods.

Example fix

// before
session.EndTransactional(); // throws — never began

// after
using (session.BeginTransactional())
{
    /* ... transactional work ... */
} // disposes/ends the transaction
Defensive patterns

Strategy: validation

Validate before calling

// Before calling EndTransactional or transactional ops:
if (!isInTransaction) throw new InvalidOperationException("BeginTransactional must be called first.");

Try / catch

try { session.BeginTransactional(); /* ... */ }
catch (TsavoriteException ex) when (ex.Message.Contains("BeginTransactional has not been called"))
{ /* log and ensure BeginTransactional is called first */ }

Prevention

When it happens

Trigger: Calling a transactional operation (e.g. EndTransactional, or a lock operation that checks transactional state) without first calling BeginTransactional on the session.

Common situations: Calling EndTransactional without a matching BeginTransactional; lock operations invoked outside a transactional scope; session reused across transactions with missing BeginTransactional calls.

Related errors


AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13). Data as JSON: /api/errors/b0444ee9ab3e70bc. Report an issue: GitHub.