{"record":{"id":"897983406dab12e7","repo":"stride3d/stride","slug":"this-operation-is-already-in-progress","errorCode":null,"errorMessage":"This operation is already in progress","messagePattern":"This operation is already in progress","errorType":"exception","errorClass":"TransactionException","httpStatus":null,"severity":"error","filePath":"sources/core/Stride.Core.Design/Transactions/Operation.cs","lineNumber":84,"sourceCode":"    }\n\n    /// <inheritdoc/>\n    void IOperation.Freeze()\n    {\n        if (IsFrozen)\n            throw new TransactionException(\"This operation is already frozen.\");\n\n        FreezeContent();\n        IsFrozen = true;\n    }\n\n    /// <inheritdoc/>\n    void IOperation.Rollback()\n    {\n        if (IsFrozen)\n            throw new TransactionException(\"A disposed operation cannot be rollbacked.\");\n        if (inProgress)\n            throw new TransactionException(\"This operation is already in progress\");\n\n        inProgress = true;\n        Rollback();\n        inProgress = false;\n    }\n\n    /// <inheritdoc/>\n    void IOperation.Rollforward()\n    {\n        if (IsFrozen)\n            throw new TransactionException(\"A disposed operation cannot be rollforwarded.\");\n        if (inProgress)\n            throw new TransactionException(\"This operation is already in progress\");\n\n        inProgress = true;\n        Rollforward();\n        inProgress = false;\n    }","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/core/Stride.Core.Design/Transactions/Operation.cs#L66-L102","documentation":"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.","triggerScenarios":"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.","commonSituations":"Rollback handlers that re-enter the transaction API, multi-threaded access to a shared operation without synchronization.","solutions":["Avoid re-entrant Rollback/Rollforward calls from within rollback handlers","Serialize access to the operation with a lock if used from multiple threads","Wait for the in-progress operation to finish before issuing another one"],"exampleFix":"// before (re-entrant from handler)\nvoid OnOperationEvent() => operation.Rollback(); // may recurse\n// after\nlock (operationLock)\n{\n    if (!inRollback) operation.Rollback();\n}","handlingStrategy":"retry","validationCode":"lock (operationLock)\n{\n    if (operationInProgress) return; // or queue the request\n}","typeGuard":"bool IsOperationIdle(Operation o) => !o.IsFrozen; // plus external in-progress tracking","tryCatchPattern":"try { operation.Rollback(); }\ncatch (TransactionException ex) when (ex.Message == \"This operation is already in progress\")\n{\n    // retry after the current rollback finishes, or queue\n}","preventionTips":["Never call Rollback re-entrantly from rollback event handlers","Synchronize multi-threaded operation access with a lock","Queue undo requests instead of issuing them concurrently"],"tags":["transaction","rollback","concurrency"],"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-15T23:17:13.987Z"}