stride3d/stride · error · InvalidOperationException

A transaction failed to be created.

Error message

A transaction failed to be created.

What it means

SetValue starts an undo transaction before mutating state and, in the finally block, requires that a transaction was actually created whenever transaction creation was requested. If it is still null, something is wrong inside the undo/redo service, and the library fails with InvalidOperationException to expose the broken invariant rather than silently skipping undo tracking.

Solutions

  1. Fix or replace the IUndoRedoService so CreateTransaction always returns a valid transaction when not already in undo/redo
  2. Check that no exception path in the try block aborts transaction creation silently
  3. Ensure the service is the stock UndoRedoService or a well-tested implementation
  4. Log/diagnose why CreateTransaction yielded null
Defensive patterns

Strategy: try-catch

Validate before calling

if (undoRedoService is not UndoRedoService) throw new InvalidOperationException("Unverified IUndoRedoService in use.");

Try / catch

try { vm.Update(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("transaction failed to be created"))
{ log.LogError(ex, "UndoRedoService.CreateTransaction returned null"); }

Prevention

When it happens

Trigger: UndoRedoService.UndoRedoInProgress is false and createTransaction is true, but the service's CreateTransaction returned null or threw before assignment, leaving the transaction variable null in the finally block.

Common situations: A custom or broken IUndoRedoService implementation returning null; an exception thrown between transaction creation and use; misconfigured service provider returning a degenerate undo service.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/1cafa3465c29e8cb. Report an issue: GitHub.

Appendix: source

Thrown at sources/presentation/Stride.Core.Presentation/ViewModels/EditableViewModel.cs:232

        if (!EqualityComparer<T>.Default.Equals(field, value))
        {
            ITransaction? transaction = null;
            if (!UndoRedoService.UndoRedoInProgress && createTransaction)
            {
                transaction = UndoRedoService.CreateTransaction();
                var concatPropertyName = string.Join(", ", propertyNames.Where(x => !uncancellableChanges.Contains(x)).Select(s => $"'{s}'"));
                UndoRedoService.SetName(transaction, $"Update property {concatPropertyName}");
            }
            try
            {
                return base.SetValue(ref field, value, updateAction, propertyNames);
            }
            finally
            {
                if (!UndoRedoService.UndoRedoInProgress && createTransaction)
                {
                    if (transaction == null)
                        throw new InvalidOperationException("A transaction failed to be created.");
                    transaction.Complete();
                }
            }
        }
        return false;
    }

    private bool SetValue(Func<bool>? hasChangedFunction, Action? updateAction, bool createTransaction, params string[] propertyNames)
    {
        if (propertyNames.Length == 0)
            throw new ArgumentOutOfRangeException(nameof(propertyNames), "This method must be invoked with at least one property name.");

        if (hasChangedFunction == null || hasChangedFunction())
        {
            ITransaction? transaction = null;
            if (!UndoRedoService.UndoRedoInProgress && createTransaction)
            {
                transaction = UndoRedoService.CreateTransaction();

View on GitHub (pinned to 96fad776d2)