{"record":{"id":"498da954d1608626","repo":"elsa-workflows/elsa-core","slug":"iworkflowcommitnotificationscope","errorCode":null,"errorMessage":"IWorkflowCommitNotificationScope","messagePattern":"IWorkflowCommitNotificationScope","errorType":"exception","errorClass":"ObjectDisposedException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.Workflows.Runtime/Services/WorkflowCommitNotificationBuffer.cs","lineNumber":85,"sourceCode":"\n            if (exceptions is { Count: > 0 })\n                throw new AggregateException(\"One or more workflow commit notifications failed.\", exceptions);\n        }\n\n        public void Dispose()\n        {\n            if (_disposed)\n                return;\n\n            _disposed = true;\n            if (ReferenceEquals(owner._currentScope.Value, this))\n                owner._currentScope.Value = parent;\n        }\n\n        private void ThrowIfDisposed()\n        {\n            if (_disposed)\n                throw new ObjectDisposedException(nameof(IWorkflowCommitNotificationScope));\n        }\n    }\n\n    private record Entry(INotification Notification, IEventPublishingStrategy? Strategy);\n}\n","sourceCodeStart":67,"sourceCodeEnd":91,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Workflows.Runtime/Services/WorkflowCommitNotificationBuffer.cs#L67-L91","documentation":"The scope object implementing IWorkflowCommitNotificationScope guards against use after disposal. ThrowIfDisposed throws ObjectDisposedException(nameof(IWorkflowCommitNotificationScope)) when any member is accessed after the scope was disposed (e.g. after the workflow commit completed).","triggerScenarios":"Holding a reference to an IWorkflowCommitNotificationScope (obtained via BeginScope-like APIs) and calling methods on it after Dispose, such as enqueueing notifications from a background task that outlives the workflow execution.","commonSituations":"Fire-and-forget tasks capturing the scope and writing notifications after the workflow finished; using a scope stored in a long-lived field; accidentally disposing the scope in a using block while still enqueuing afterwards.","solutions":["Ensure all notification enqueueing happens within the scope's lifetime (before Dispose / before the workflow completes).","Do not capture the scope in background tasks; capture the data and enqueue synchronously during the commit.","Check _disposed (IsDisposed) before using the scope if lifetime is uncertain.","Restructure so the scope follows the using/RAII pattern tightly around the code that needs it."],"exampleFix":"// before\n_scope = notificationScope; // stored in field, used after commit\n\n// after\nusing var scope = await factory.BeginScopeAsync(ct);\nscope.Enqueue(notification, strategy);\n// scope disposed here; never used afterwards","handlingStrategy":"try-catch","validationCode":"if (scope is IDisposable d && /* track disposed flag */ false) { /* check before use */ }","typeGuard":"bool ScopeUsable(IWorkflowCommitNotificationScope scope) => scope is not { } disposedScope || !IsDisposed(disposedScope); // prefer storing your own _disposed flag","tryCatchPattern":"try\n{\n    scope.Enqueue(notification, strategy);\n}\ncatch (ObjectDisposedException)\n{\n    logger.LogWarning(\"Commit notification scope already disposed; notification dropped.\");\n}","preventionTips":["Scope all notification enqueues inside the scope's using lifetime","Never capture the scope in fire-and-forget tasks","Enqueue synchronously before workflow commit completes"],"tags":["disposed","scope","runtime","object-lifetime"],"backgroundTag":"unsupported-operation","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}