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

  1. Create a new AssetSourceTracker instance instead of reusing a disposed one
  2. Check IsDisposed (or track disposal yourself) before setting IsTracking
  3. Reorder code so tracking is enabled before disposal, and disable tracking rather than disposing if it may be re-enabled
  4. 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

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


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)