{"record":{"id":"ac8eaa797c5abb4e","repo":"stride3d/stride","slug":"unable-to-rollforward-a-rollback-or-rollforward-operation-is","errorCode":null,"errorMessage":"Unable to rollforward. A rollback or rollforward operation is already in progress.","messagePattern":"Unable to rollforward\\. 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":249,"sourceCode":"                lastTransaction.Interface.Rollback();\n            }\n            finally\n            {\n                RollInProgress = false;\n            }\n            TransactionRollbacked?.Invoke(this, new TransactionEventArgs(lastTransaction));\n        }\n    }\n\n    /// <inheritdoc/>\n    public void Rollforward()\n    {\n        lock (lockObject)\n        {\n            if (!CanRollforward)\n                throw new TransactionException(\"Unable to rollforward. This method cannot be invoked when CanRollforward is false.\");\n            if (RollInProgress)\n                throw new TransactionException(\"Unable to rollforward. 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.Rollforward();\n            }\n            finally\n            {\n                RollInProgress = false;\n            }\n            TransactionRollforwarded?.Invoke(this, new TransactionEventArgs(lastTransaction));\n        }\n    }\n\n    public void Resize(int newCapacity)","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/core/Stride.Core.Design/Transactions/TransactionStack.cs#L231-L267","documentation":"Rollforward() rejects re-entrant or concurrent invocation while another rollback/rollforward is running; the stack sets RollInProgress for the duration and this guard prevents the redo loop from being corrupted. This error is thrown when Rollforward is called while RollInProgress is already true.","triggerScenarios":"Calling Rollforward() from inside a transaction's Rollforward/Rollback handler (re-entrancy), or from another thread while a rollback/rollforward is in progress.","commonSituations":"Redo handlers that themselves trigger redo via property-change feedback loops; queued UI commands firing Rollforward twice; multithreaded redo calls.","solutions":["Never call Rollforward/Rollback from inside transaction rollback/rollforward handlers.","Serialize undo/redo calls onto one thread or guard them with a lock/flag.","Suppress re-entrant notifications while a roll operation is running (guard flags in event handlers).","Check transactionStack.RollInProgress before invoking Rollforward."],"exampleFix":"// before\nvoid OnTransactionRolledForward() => stack.Rollforward(); // re-entrant\n\n// after\nvoid OnTransactionRolledForward()\n{\n    if (!stack.RollInProgress)\n        stack.Rollforward();\n}","handlingStrategy":"validation","validationCode":"if (!transactionStack.RollInProgress && transactionStack.CanRollforward)\n    transactionStack.Rollforward();","typeGuard":"bool IsRollIdle(TransactionStack stack) => !stack.RollInProgress;","tryCatchPattern":"try\n{\n    transactionStack.Rollforward();\n}\ncatch (TransactionException ex) when (ex.Message.Contains(\"already in progress\"))\n{\n    // re-entrant or concurrent call; ignore or defer\n}","preventionTips":["Never 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"}