{"record":{"id":"3632270048fff1e9","repo":"stride3d/stride","slug":"unable-to-rollback-a-rollback-or-rollforward-operation-is","errorCode":null,"errorMessage":"Unable to rollback. A rollback or rollforward operation is already in progress.","messagePattern":"Unable to rollback\\. A rollback or rollforward operation is already in progress\\.","errorType":"exception","errorClass":"TransactionException","httpStatus":null,"severity":"error","filePath":"sources/core/Stride.Core.Design/Transactions/TransactionStack.cs","lineNumber":223,"sourceCode":"                // Complete parent transactions\n                if ((transaction.Flags & TransactionFlags.KeepParentsAlive) != 0)\n                {\n                    foreach (var parentTransaction in transactionsInProgress.Reverse())\n                        parentTransaction.Complete();\n                }\n            }\n        }\n    }\n\n    /// <inheritdoc/>\n    public void Rollback()\n    {\n        lock (lockObject)\n        {\n            if (!CanRollback)\n                throw new TransactionException(\"Unable to rollback. This method cannot be invoked when CanRollback is false.\");\n            if (RollInProgress)\n                throw new TransactionException(\"Unable to rollback. A rollback or rollforward operation is already in progress.\");\n            if (transactionsInProgress.Count > 0)\n                throw new TransactionException(\"Unable to rollback. A transaction is in progress.\");\n\n            var lastTransaction = transactions[--currentPosition];\n            RollInProgress = true;\n            try\n            {\n                lastTransaction.Interface.Rollback();\n            }\n            finally\n            {\n                RollInProgress = false;\n            }\n            TransactionRollbacked?.Invoke(this, new TransactionEventArgs(lastTransaction));\n        }\n    }\n\n    /// <inheritdoc/>","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/core/Stride.Core.Design/Transactions/TransactionStack.cs#L205-L241","documentation":"Rollback() rejects re-entrant or concurrent invocation while another rollback/rollforward is running; the stack sets RollInProgress during the operation and this guard prevents corrupting the position while a rollback loop is active. This error is thrown when Rollback is called while RollInProgress is already true.","triggerScenarios":"Calling Rollback() from inside a transaction's Rollback handler (re-entrancy), or from another thread while a rollback/rollforward is in progress.","commonSituations":"Undo handlers that themselves trigger undo (e.g. property-change feedback loops); UI double-clicks or queued commands firing Rollback twice; multithreaded code calling Rollback concurrently.","solutions":["Never call Rollback/Rollforward from inside transaction rollback/rollforward handlers.","Serialize undo/redo calls onto a single thread or use a lock/flag around stack operations.","Suppress re-entrant notifications while a rollback is running (guard flags in event handlers).","Check transactionStack.RollInProgress before invoking Rollback."],"exampleFix":"// before\nvoid OnTransactionRolledBack() => stack.Rollback(); // re-entrant\n\n// after\nvoid OnTransactionRolledBack()\n{\n    if (!stack.RollInProgress)\n        stack.Rollback();\n}","handlingStrategy":"validation","validationCode":"if (!transactionStack.RollInProgress && transactionStack.CanRollback)\n    transactionStack.Rollback();","typeGuard":"bool IsRollIdle(TransactionStack stack) => !stack.RollInProgress;","tryCatchPattern":"try\n{\n    transactionStack.Rollback();\n}\ncatch (TransactionException ex) when (ex.Message.Contains(\"already in progress\"))\n{\n    // re-entrant or concurrent call; ignore or defer\n}","preventionTips":["Never undo/redo from inside transaction handlers.","Use a re-entrancy guard flag in event handlers.","Funnel all undo/redo calls through one dispatcher/thread."],"tags":["transactions","undo-redo","reentrancy"],"backgroundTag":"invalid-state-transition","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}