spectreconsole/spectre.console · error · InvalidOperationException

Stopped tasks cannot be restarted

Error message

Stopped tasks cannot be restarted

What it means

Thrown by ProgressTask.StartTask() when StopTime is already set (the task has been stopped/finished). ProgressTask follows a one-way lifecycle: created → started → stopped. Once StopTask() sets StopTime, the task is permanently finished and StartTask() refuses to reset it. This InvalidOperationException prevents confusing speed/duration calculations and state corruption.

Source

Thrown at src/Spectre.Console/Live/Progress/ProgressTask.cs:159

        {
            throw new ArgumentException("Task name cannot be empty", nameof(description));
        }

        Id = id;
        State = new ProgressTaskState();
        StartTime = autoStart ? _timeProvider.GetLocalNow().LocalDateTime : null;
    }

    /// <summary>
    /// Starts the task.
    /// </summary>
    public void StartTask()
    {
        lock (_lock)
        {
            if (StopTime != null)
            {
                throw new InvalidOperationException("Stopped tasks cannot be restarted");
            }

            StartTime = _timeProvider.GetLocalNow().LocalDateTime;
            StopTime = null;
        }
    }

    /// <summary>
    /// Stops and marks the task as finished.
    /// </summary>
    public void StopTask()
    {
        lock (_lock)
        {
            var now = _timeProvider.GetLocalNow().LocalDateTime;
            StartTime ??= now;
            StopTime = now;
        }

View on GitHub (pinned to 0acc92fada)

Solutions

  1. Create a new ProgressTask for each operation rather than restarting a stopped one.
  2. Check task.IsFinished before calling StartTask (IsFinished is true when StopTime is set).
  3. Track task lifecycle state in your code to avoid calling StartTask after StopTask.

Example fix

// before
task.StopTask();
// ... later
task.StartTask(); // throws

// after
task.StopTask();
task = ctx.AddTask("New operation"); // fresh task
Defensive patterns

Strategy: validation

Validate before calling

// Check lifecycle before restarting
if (task.IsFinished)
{
    // Create a new task instead of restarting
    task = ctx.AddTask(description);
}
else
{
    task.StartTask();
}

Type guard

static bool CanRestart(ProgressTask task) => !task.IsFinished;

Try / catch

try
{
    task.StartTask();
}
catch (InvalidOperationException ex) when (ex.Message.Contains("cannot be restarted"))
{
    // Create a fresh task
    task = ctx.AddTask(task.Description);
}

Prevention

When it happens

Trigger: Calling task.StopTask() followed by task.StartTask() on the same ProgressTask instance. Also triggered by code that reuses ProgressTask objects in a loop or pool. Calling StartTask on an auto-started task that was already stopped by the framework.

Common situations: Loop that reuses a ProgressTask for multiple operations; retry logic that tries to restart a stopped task; event-driven code where StopTask and StartTask can race.

Related errors


AI-assisted analysis of spectreconsole/spectre.console@0acc92fada (2026-08-13). Data as JSON: /api/errors/a1afb3a9fc38758b. Report an issue: GitHub.