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
- Ensure only one tracking loop is active per controller: call Dispose/StopTracking before starting a new tracking session.
- If overriding OnGameStarted in a plugin, do not call StartTracking if the base controller already handles tracking.
- Use a null-check on watcherToken before calling StartTracking in custom code.
- 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
- Expose an IsTracking property backed by watcherToken and check it before StartTracking.
- Ensure StopTracking/Dispose is called in the OnGameStopped handler before any new StartTracking.
- Do not call StartTracking from plugin event handlers if the base controller already tracks.
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
- Emulator not found.
- Uknown play action configuration.
- Can't find built-in {builtIn.BuiltInProfileName} emulator pr
- Emulator startup script not found.
- Uknown play action type.
AI-assisted analysis of JosefNemec/Playnite@5911f4e964 (2026-08-13).
Data as JSON: /api/errors/3ff886f62c8a476b.
Report an issue: GitHub.