stride3d/stride · error · TransactionException

This operation is already in progress

Error message

This operation is already in progress

What it means

Reentrancy guard in Operation.Rollback: the operation is already mid-rollback (the inProgress flag is set), meaning Rollback was called recursively or concurrently on the same operation. Transaction operations are not reentrant to keep undo state consistent.

Solutions

  1. Avoid re-entrant Rollback/Rollforward calls from within rollback handlers
  2. Serialize access to the operation with a lock if used from multiple threads
  3. Wait for the in-progress operation to finish before issuing another one

Example fix

// before (re-entrant from handler)
void OnOperationEvent() => operation.Rollback(); // may recurse
// after
lock (operationLock)
{
    if (!inRollback) operation.Rollback();
}
Defensive patterns

Strategy: retry

Validate before calling

lock (operationLock)
{
    if (operationInProgress) return; // or queue the request
}

Type guard

bool IsOperationIdle(Operation o) => !o.IsFrozen; // plus external in-progress tracking

Try / catch

try { operation.Rollback(); }
catch (TransactionException ex) when (ex.Message == "This operation is already in progress")
{
    // retry after the current rollback finishes, or queue
}

Prevention

When it happens

Trigger: Calling IOperation.Rollback() recursively or concurrently while a prior Rollback()/Rollforward() call on the same operation has not returned — typically via re-entrancy from events raised during rollback.

Common situations: Rollback handlers that re-enter the transaction API, multi-threaded access to a shared operation without synchronization.

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/897983406dab12e7. Report an issue: GitHub.

Appendix: source

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

    }

    /// <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();
        inProgress = false;
    }

View on GitHub (pinned to 96fad776d2)