stride3d/stride · error · TransactionException

A disposed operation cannot be rollbacked.

Error message

A disposed operation cannot be rollbacked.

What it means

Thrown by IOperation.Rollback when the operation is frozen (the flag the library sets on completion/dispose, treated here as 'disposed'). A finalized operation can no longer undo its changes, so rolling it back is rejected.

Solutions

  1. Do not roll back operations from completed transactions; capture the rollback need before Complete()
  2. Check the operation/transaction state before invoking Rollback
  3. Create a new compensating transaction to undo committed work

Example fix

// before
transaction.Complete();
operation.Rollback(); // throws
// after
if (!operation.IsFrozen)
    operation.Rollback();
Defensive patterns

Strategy: type-guard

Validate before calling

if (operation.IsFrozen)
    throw new InvalidOperationException("Cannot roll back a completed operation");

Type guard

bool CanRollback(IOperation op) => op is Operation o && !o.IsFrozen;

Try / catch

try { operation.Rollback(); }
catch (TransactionException ex) when (ex.Message.Contains("disposed operation cannot be rollbacked"))
{
    logger.LogWarning("Attempted rollback of completed operation");
}

Prevention

When it happens

Trigger: Calling IOperation.Rollback() on an operation after its transaction was completed/disposed (IsFrozen == true), e.g. keeping a reference to an operation and attempting an undo after transaction completion.

Common situations: Undo stacks retaining stale operations after commit, rollback triggered from an event handler after Dispose, reusing completed transactions.

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/991955c2f6c340b6. Report an issue: GitHub.

Appendix: source

Thrown at sources/core/Stride.Core.Design/Transactions/Operation.cs:82

    {
        return false;
    }

    /// <inheritdoc/>
    void IOperation.Freeze()
    {
        if (IsFrozen)
            throw new TransactionException("This operation is already frozen.");

        FreezeContent();
        IsFrozen = true;
    }

    /// <inheritdoc/>
    void IOperation.Rollback()
    {
        if (IsFrozen)
            throw new TransactionException("A disposed operation cannot be rollbacked.");
        if (inProgress)
            throw new TransactionException("This operation is already in progress");

        inProgress = true;
        Rollback();
        inProgress = false;
    }

    /// <inheritdoc/>
    void IOperation.Rollforward()
    {
        if (IsFrozen)
            throw new TransactionException("A disposed operation cannot be rollforwarded.");
        if (inProgress)
            throw new TransactionException("This operation is already in progress");

        inProgress = true;
        Rollforward();

View on GitHub (pinned to 96fad776d2)