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
- Create a new ProgressTask for each operation rather than restarting a stopped one.
- Check task.IsFinished before calling StartTask (IsFinished is true when StopTime is set).
- 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
- Never reuse stopped ProgressTask instances — create new ones for each operation.
- Check IsFinished before calling StartTask.
- Track task lifecycle in your orchestration code.
- Design progress loops that allocate fresh tasks per unit of work.
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
- Task name cannot be empty.
- At least one column must be specified.
- Task name cannot be empty
- Cannot export text since a recording hasn't been started.
- Cannot export HTML since a recording hasn't been started.
AI-assisted analysis of spectreconsole/spectre.console@0acc92fada (2026-08-13).
Data as JSON: /api/errors/a1afb3a9fc38758b.
Report an issue: GitHub.