stride3d/stride · error · TransactionException

This operation is already frozen.

Error message

This operation is already frozen.

What it means

Thrown by IOperation.Freeze when the operation's IsFrozen flag is already set. Freezing is a one-time lifecycle step that finalizes the operation's content; freezing twice would violate the transaction system's invariants.

Solutions

  1. Freeze each operation only once; guard with an IsFrozen check before calling
  2. Create a new operation instead of reusing a frozen one
  3. Track whether the owning transaction was already completed before calling Complete()

Example fix

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

Strategy: try-catch

Validate before calling

if (operation.IsFrozen)
    return; // already finalized, nothing to do

Type guard

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

Try / catch

try { operation.Freeze(); }
catch (TransactionException ex) when (ex.Message == "This operation is already frozen.")
{
    // idempotent: safe to ignore
}

Prevention

When it happens

Trigger: Calling IOperation.Freeze() (or triggering it implicitly via transaction completion/dispose) on an Operation instance that was already frozen, e.g. completing a transaction twice or sharing one operation between transactions.

Common situations: Calling Complete()/Dispose() on a transaction more than once, reusing a single operation object across multiple transaction commits.

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/5ff4638c3cc524f5. Report an issue: GitHub.

Appendix: source

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

    /// <summary>
    /// Freezes the content of this operation, forbidding any subsequent rollback and rollforward.
    /// </summary>
    /// <remarks>This operation should release any reference that is not needed anymore by the operation.</remarks>
    protected virtual void FreezeContent()
    {
        // Do nothing by default
    }

    protected virtual bool MergeInto(Operation otherOperation)
    {
        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;
    }

View on GitHub (pinned to 96fad776d2)