stride3d/stride · error · TransactionException

The transaction being completed is not that last created…

Error message

The transaction being completed is not that last created transaction.

What it means

EndTransaction verifies that the transaction being completed is the one on top of the transactionsInProgress stack (LIFO order). This error is thrown when the passed transaction does not match the top of the stack, i.e. an inner transaction is still open or the wrong transaction reference was supplied.

Solutions

  1. Always close transactions in reverse creation order (LIFO): end the innermost transaction first.
  2. Audit nested CreateTransaction/EndTransaction pairs to guarantee strict nesting.
  3. Ensure no inner transaction is leaked when exceptions occur (use try/finally inside each scope).
  4. Verify you pass the same transaction instance returned by the most recent CreateTransaction/PushTransaction.

Example fix

// before
stack.EndTransaction(outerTx); // innerTx still open

// after
stack.EndTransaction(innerTx); // close innermost first
stack.EndTransaction(outerTx);
Defensive patterns

Strategy: validation

Validate before calling

// ensure LIFO order: only end the innermost transaction
if (transactionStack.TransactionInProgress && transaction == innermostOpenTransaction)
    transactionStack.EndTransaction(transaction);

Type guard

bool IsInnermost(Transaction transaction, Transaction innermost) => ReferenceEquals(transaction, innermost);

Try / catch

try
{
    transactionStack.EndTransaction(transaction);
}
catch (TransactionException ex) when (ex.Message.Contains("not that last created transaction"))
{
    // find and close the leaked inner transaction first
}

Prevention

When it happens

Trigger: Calling EndTransaction(outerTransaction) while an inner nested transaction (created later) is still open; completing transactions out of creation order; passing a transaction from a different TransactionStack instance.

Common situations: Nested transaction scoping closed in the wrong order (crossing begin/end pairs); leaked inner transactions on exception paths; caching transaction objects and completing them later out of order.

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 stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/5671e41139c9cf87. Report an issue: GitHub.

Appendix: source

Thrown at sources/core/Stride.Core.Design/Transactions/TransactionStack.cs:142

    }

    /// <inheritdoc/>
    public IEnumerable<IReadOnlyTransaction> RetrieveAllTransactions()
    {
        return transactions;
    }

    public void CompleteTransaction(Transaction transaction)
    {
        lock (lockObject)
        {
            try
            {
                if (transactionsInProgress.Count == 0)
                    throw new TransactionException("There is not transaction in progress in the transaction stack.");

                if (transaction != transactionsInProgress.Pop())
                    throw new TransactionException("The transaction being completed is not that last created transaction.");

                // Check if we're completing the last transaction
                TransactionInProgress = transactionsInProgress.Count > 0;

                // Ignore the transaction if it is empty
                if (transaction.IsEmpty)
                    return;

                // If this transaction has no effect, discard it.
                if (transaction.Operations.All(x => !x.HasEffect))
                    return;

                // If we're not the last transaction, consider this transaction as an operation of its parent transaction
                if (TransactionInProgress)
                {
                    // Avoid useless nested transaction if we have a single operation inside.
                    PushOperation(transaction.Operations.Count == 1 ? transaction.Operations.Single() : transaction);
                    return;

View on GitHub (pinned to 96fad776d2)