stride3d/stride · error · InvalidOperationException
Cannot enable tracking when this instance is disposed
Error message
Cannot enable tracking when this instance is disposed
What it means
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.
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
Example fix
// before
tracker.Dispose();
tracker.IsTracking = true; // throws
// after
if (!tracker.IsDisposed)
{
tracker.IsTracking = true;
}
else
{
tracker = new AssetSourceTracker(tracker.PackageSession);
tracker.IsTracking = true;
} Defensive patterns
Strategy: validation
Validate before calling
if (tracker.IsDisposed)
throw new InvalidOperationException("Tracker disposed; create a new AssetSourceTracker");
tracker.IsTracking = true; Type guard
bool CanToggleTracking(AssetSourceTracker t) => t != null && !t.IsDisposed;
Try / catch
try { tracker.IsTracking = true; }
catch (InvalidOperationException) when (tracker.IsDisposed)
{
tracker = new AssetSourceTracker(session);
tracker.IsTracking = true;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- TrackingSleepTime must be > 0
- This operation is not supported by the source tracker.
- The order of the Asset.Id property must be lower than the…
- Event handlers can't be added or removed after the…
- RoutingSerializer expected in the chain of serializers
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/2991facafa6444b9.
Report an issue: GitHub.
Appendix: source
Thrown at sources/assets/Stride.Core.Assets/Tracking/AssetSourceTracker.cs:70
/// Gets a source dataflow block in which notifications that a source file has changed are pushed.
/// </summary>
public BroadcastBlock<IReadOnlyList<SourceFileChangedData>> SourceFileChanged { get; } = new BroadcastBlock<IReadOnlyList<SourceFileChangedData>>(null);
/// <summary>
/// Gets or sets a value indicating whether this instance should track file disk changed events. Default is <c>false</c>
/// </summary>
/// <value><c>true</c> if this instance should track file disk changed events; otherwise, <c>false</c>.</value>
public bool EnableTracking
{
get
{
return fileEventThreadHandler != null;
}
set
{
if (isDisposed)
{
throw new InvalidOperationException("Cannot enable tracking when this instance is disposed");
}
lock (ThisLock)
{
if (value)
{
bool activateTracking = false;
if (DirectoryWatcher == null)
{
DirectoryWatcher = new DirectoryWatcher();
DirectoryWatcher.Modified += DirectoryWatcher_Modified;
activateTracking = true;
}
if (fileEventThreadHandler == null)
{
fileEventThreadHandler = new Thread(SafeAction.Wrap(RunChangeWatcher)) { IsBackground = true, Name = "RunChangeWatcher thread" };
fileEventThreadHandler.Start();View on GitHub (pinned to 96fad776d2)