JosefNemec/Playnite · error · Exception

Game is already being tracked.

Error message

Game is already being tracked.

What it means

Thrown as a generic Exception when StartTracking is called while a tracking task is already active (watcherToken != null). The controller uses a single CancellationTokenSource to manage one concurrent tracking loop per game controller instance. Calling StartTracking a second time without first stopping the previous tracker is a lifecycle violation.

Source

Thrown at source/Playnite/Controllers/GenericGameController.cs:680

                }
                else
                {
                    throw new NotSupportedException();
                }
            }
        }

        public void StartTracking(
            Func<bool> trackingAction,
            Func<int> startupCheck = null,
            Action<int> gameStartedAction = null,
            Action gameStoppedAction = null,
            int trackingFrequency = 2000,
            int trackingStartDelay = 0)
        {
            if (watcherToken != null)
            {
                throw new Exception("Game is already being tracked.");
            }

            watcherToken = new CancellationTokenSource();
            Task.Run(async () =>
            {
                ulong playTimeMs = 0;
                var trackingWatch = new Stopwatch();
                var maxFailCount = 5;
                var failCount = 0;

                if (trackingStartDelay > 0)
                {
                    await Task.Delay(trackingStartDelay, watcherToken.Token).ContinueWith(task => { });
                }

                if (startupCheck != null)
                {
                    while (true)

View on GitHub (pinned to 5911f4e964)

Solutions

  1. Ensure only one tracking loop is active per controller: call Dispose/StopTracking before starting a new tracking session.
  2. If overriding OnGameStarted in a plugin, do not call StartTracking if the base controller already handles tracking.
  3. Use a null-check on watcherToken before calling StartTracking in custom code.
  4. Ensure game controllers are properly disposed and recreated per game session, not reused.

Example fix

// before — calling StartTracking without checking existing state
public void StartCustomTracking()
{
    controller.StartTracking(() => IsRunning(), trackingFrequency: 2000);
}

// after — guard against double-tracking
public void StartCustomTracking()
{
    if (controller.IsTrackingActive) // check watcherToken state
    {
        logger.Warn("Tracking already active, skipping.");
        return;
    }
    controller.StartTracking(() => IsRunning(), trackingFrequency: 2000);
}
Defensive patterns

Strategy: validation

Validate before calling

if (controller.IsTracking) // expose watcherToken != null as a property
{
    logger.Warn("Tracking already active; stopping previous tracker.");
    controller.StopTracking();
}

Try / catch

try
{
    controller.StartTracking(() => IsRunning());
}
catch (Exception ex) when (ex.Message.Contains("already being tracked"))
{
    logger.Warn("Attempted to start duplicate tracking; ignoring.");
}

Prevention

When it happens

Trigger: StartTracking is invoked twice on the same GenericGameController instance without calling the stop/dispose path in between. This can occur if a game's start sequence triggers multiple tracking setups (e.g., both Process and Directory tracking modes), or if a plugin manually calls StartTracking after the controller already started its own.

Common situations: A custom plugin overrides OnGameStarted and calls StartTracking in addition to the base controller. A race condition where the game is launched twice rapidly. A tracking mode configuration triggers overlapping StartTracking calls. The controller was not properly disposed between game sessions.

Related errors


AI-assisted analysis of JosefNemec/Playnite@5911f4e964 (2026-08-13). Data as JSON: /api/errors/3ff886f62c8a476b. Report an issue: GitHub.