HumanSignal/label-studio · warning · TransitionValidationError
Cannot start an already completed task
Error message
Cannot start an already completed task
What it means
The example Task 'start' transition's validate_transition raises TransitionValidationError when the task's current state is already COMPLETED. Business rule: a completed task cannot be restarted via the start transition.
Source
Thrown at label_studio/fsm/transitions.py:110
class BaseTransition(BaseModel, ABC, Generic[EntityType, StateModelType]):
"""
Abstract base class for all declarative state transitions.
This provides the framework for implementing transitions as first-class Pydantic
models with built-in validation, context handling, and execution logic.
Example usage:
class StartTaskTransition(BaseTransition[Task, TaskState]):
assigned_user_id: int = Field(..., description="User assigned to start the task")
estimated_duration: Optional[int] = Field(None, description="Estimated completion time in hours")
def get_target_state(self, context: Optional[TransitionContext[Task, TaskState]]) -> str:
return TaskStateChoices.IN_PROGRESS
def validate_transition(self, context: TransitionContext[Task, TaskState]) -> bool:
if context.current_state == TaskStateChoices.COMPLETED:
raise TransitionValidationError("Cannot start an already completed task")
return True
def transition(self, context: TransitionContext[Task, TaskState]) -> Dict[str, Any]:
return {
"assigned_user_id": self.assigned_user_id,
"estimated_duration": self.estimated_duration,
"started_at": context.timestamp.isoformat()
}
"""
model_config = ConfigDict(arbitrary_types_allowed=True, validate_assignment=True, use_enum_values=True)
def __init__(self, **data):
super().__init__(**data)
self.__context: Optional[TransitionContext[EntityType, StateModelType]] = None
@property
def context(self) -> Optional[TransitionContext[EntityType, StateModelType]]:View on GitHub (pinned to 0b49e9b539)
Solutions
- Check the task's current state before dispatching the transition; skip if already COMPLETED.
- Catch TransitionValidationError and surface 'task already completed' to the user instead of a generic error.
- Guard the start transition's target state: use a restart/redo transition for completed tasks if restarting is intended.
- Add idempotency handling so duplicate start requests are no-ops.
Example fix
// before
execute_transition(task, 'start')
// after
if get_current_state(task) != TaskStateChoices.COMPLETED:
execute_transition(task, 'start')
else:
raise TransitionValidationError('Cannot start an already completed task') Defensive patterns
Strategy: validation
Validate before calling
current = get_current_state(task)
if current == TaskStateChoices.COMPLETED:
raise TransitionValidationError('Cannot start an already completed task') Type guard
def is_restartable(task) -> bool:
return get_current_state(task) != TaskStateChoices.COMPLETED Try / catch
try:
execute_transition(task, 'start')
except TransitionValidationError:
notify_user('This task is already completed and cannot be started again.') Prevention
- Disable start actions in the UI when the task is COMPLETED.
- Make start requests idempotent (dedupe by task+user).
- Refresh entity state before dispatching transitions from stale clients.
When it happens
Trigger: Executing the start transition on a Task whose TaskState.current_state == TaskStateChoices.COMPLETED.
Common situations: Double-clicking a start/assign button in a UI so the transition fires twice; stale client state re-submitting a start after completion; retry logic replaying an old transition request.
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
- No state model registered for {entity._meta.model_name} when
- No state model registered for entity '{entity_name}'
- Invalid entity name: {entity_name}
- transition_name: Unknown transition for this entity
- transition_name: Transition is auto-triggered and cannot be
AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29).
Data as JSON: /api/errors/ecb0f524c42edd65.
Report an issue: GitHub.