{"record":{"id":"2991facafa6444b9","repo":"stride3d/stride","slug":"cannot-enable-tracking-when-this-instance-is-disposed","errorCode":null,"errorMessage":"Cannot enable tracking when this instance is disposed","messagePattern":"Cannot enable tracking when this instance is disposed","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/assets/Stride.Core.Assets/Tracking/AssetSourceTracker.cs","lineNumber":70,"sourceCode":"    /// Gets a source dataflow block in which notifications that a source file has changed are pushed.\n    /// </summary>\n    public BroadcastBlock<IReadOnlyList<SourceFileChangedData>> SourceFileChanged { get; } = new BroadcastBlock<IReadOnlyList<SourceFileChangedData>>(null);\n\n    /// <summary>\n    /// Gets or sets a value indicating whether this instance should track file disk changed events. Default is <c>false</c>\n    /// </summary>\n    /// <value><c>true</c> if this instance should track file disk changed events; otherwise, <c>false</c>.</value>\n    public bool EnableTracking\n    {\n        get\n        {\n            return fileEventThreadHandler != null;\n        }\n        set\n        {\n            if (isDisposed)\n            {\n                throw new InvalidOperationException(\"Cannot enable tracking when this instance is disposed\");\n            }\n\n            lock (ThisLock)\n            {\n                if (value)\n                {\n                    bool activateTracking = false;\n                    if (DirectoryWatcher == null)\n                    {\n                        DirectoryWatcher = new DirectoryWatcher();\n                        DirectoryWatcher.Modified += DirectoryWatcher_Modified;\n                        activateTracking = true;\n                    }\n\n                    if (fileEventThreadHandler == null)\n                    {\n                        fileEventThreadHandler = new Thread(SafeAction.Wrap(RunChangeWatcher)) { IsBackground = true, Name = \"RunChangeWatcher thread\" };\n                        fileEventThreadHandler.Start();","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/assets/Stride.Core.Assets/Tracking/AssetSourceTracker.cs#L52-L88","documentation":"AssetSourceTracker's IsTracking property setter refuses to enable (or change) file tracking once the tracker has been disposed. The library throws InvalidOperationException because the underlying file-event watcher thread and directory watcher are torn down on dispose, so tracking can no longer function. This guards against silently 'succeeding' while no events are actually observed.","triggerScenarios":"Setting tracker.IsTracking = true (or any value) after calling tracker.Dispose(); re-enabling tracking on a cached/pooled tracker instance whose lifetime has ended; a component that owns a tracker reference but a different component already disposed it.","commonSituations":"Game editors that create/destroy asset-tracking services on workspace reload; DI containers disposing the tracker while a view model still holds a reference; forgetting that Dispose is permanent when toggling tracking on/off dynamically.","solutions":["Create a new AssetSourceTracker instance instead of reusing a disposed one","Check IsDisposed (or track disposal yourself) before setting IsTracking","Reorder code so tracking is enabled before disposal, and disable tracking rather than disposing if it may be re-enabled","Fix lifetime management so the tracker is disposed only after all consumers release it"],"exampleFix":"// before\ntracker.Dispose();\ntracker.IsTracking = true; // throws\n// after\nif (!tracker.IsDisposed)\n{\n    tracker.IsTracking = true;\n}\nelse\n{\n    tracker = new AssetSourceTracker(tracker.PackageSession);\n    tracker.IsTracking = true;\n}","handlingStrategy":"validation","validationCode":"if (tracker.IsDisposed)\n    throw new InvalidOperationException(\"Tracker disposed; create a new AssetSourceTracker\");\ntracker.IsTracking = true;","typeGuard":"bool CanToggleTracking(AssetSourceTracker t) => t != null && !t.IsDisposed;","tryCatchPattern":"try { tracker.IsTracking = true; }\ncatch (InvalidOperationException) when (tracker.IsDisposed)\n{\n    tracker = new AssetSourceTracker(session);\n    tracker.IsTracking = true;\n}","preventionTips":["Check IsDisposed before any property set on a tracker","Treat Dispose as terminal: never reuse the instance afterward","Centralize tracker creation/disposal in one owner component","Prefer IsTracking = false over Dispose when tracking may be re-enabled"],"tags":["csharp","invalidoperationexception","disposed-object","asset-tracking"],"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"}