HumanSignal/label-studio · error · ValidationError
detail: {transition validation message}
Error message
detail: {transition validation message} What it means
The transition's own validate()/guard logic raised TransitionValidationError, and FSMEntityTransitionAPI.post logs it with context and re-raises it as a DRF 400 with the extracted message. Unlike [101] this is a business-rule failure — the payload may be well-formed but the transition is not allowed for this entity's current state, user, or context (e.g., invalid state graph edge, permission-of-state rule, stale expected state).
Source
Thrown at label_studio/fsm/api.py:192
# Execute transition
StateManager = get_state_manager()
try:
state_record = StateManager.execute_transition(
entity=entity,
transition_name=transition_name,
transition_data=transition_data,
user=request.user,
organization_id=getattr(request.user, 'active_organization_id', None),
)
except PydanticValidationError as e:
# Pydantic schema validation errors from transition instantiation
raise ValidationError({'detail': extract_message(e)})
except TransitionValidationError as e:
# Explicit validation failure
logger.warning(
f'Transition validation failed with context: {e.context} and error: {e} for entity: {entity.id}'
)
raise ValidationError({'detail': extract_message(e)})
# Handle feature-flag disabled path (no state record created)
if state_record is None:
response_payload = {
'success': True,
'new_state': None,
'state_record': None,
}
else:
response_payload = {
'success': True,
'new_state': state_record.state,
# Pass model instance; nested serializer will handle representation
'state_record': state_record,
}
return Response(
FSMTransitionExecuteResponseSerializer(response_payload, context={'request': request}).data,
status=status.HTTP_200_OK,
)View on GitHub (pinned to 0b49e9b539)
Solutions
- Fetch the entity's current state (GET state history or state endpoint) and only submit transitions valid from that state.
- Read the returned detail message — it comes from the transition's own validation and names the unmet rule.
- Handle concurrent updates: re-fetch the entity and retry with a transition valid from the new state.
- If the rule is wrong for your workflow, adjust the transition's validation/guard code rather than bypassing the API.
Example fix
// before: 'detail: Cannot complete task: task is not in progress'
client.executeTransition('task', id, {'transition_name': 'complete_task'})
// after: check current state first
const state = await client.stateHistory('task', id);
if (state.results[0]?.state === 'in_progress') {
await client.executeTransition('task', id, {'transition_name': 'complete_task'});
} Defensive patterns
Strategy: try-catch
Validate before calling
from fsm.state_manager import get_state_manager
sm = get_state_manager()
current = sm.get_current_state_value(entity)
allowed = {t.name for t in sm.get_available_transitions(entity)}
assert transition_name in allowed, f'{transition_name} not valid from state {current}' Type guard
def transition_allowed(entity, transition_name: str) -> bool:
from fsm.state_manager import get_state_manager
try:
return transition_name in {t.name for t in get_state_manager().get_available_transitions(entity)}
except Exception:
return False Try / catch
from fsm.transitions import TransitionValidationError
try:
state_record = StateManager.execute_transition(entity=entity, transition_name=transition_name, ...)
except TransitionValidationError as e:
logger.warning('transition %s rejected for %s: %s', transition_name, entity.id, e)
# surface e to user; optionally re-fetch state and pick a valid transition Prevention
- Always fetch current state / available transitions before submitting a manual transition
- Make transition submission idempotent: skip if the target state is already current
- Handle concurrent-edit races by re-fetching the entity and retrying with a valid transition
When it happens
Trigger: POSTing a transition whose class raises TransitionValidationError during execution: transition not allowed from the entity's current state, guard conditions unmet, or the entity is in a state that forbids this transition. Also occurs when execute_transition's validation hooks reject with an explicit raise.
Common situations: Applying the same transition twice (entity already moved); concurrent editors causing a stale current state; transitions gated on entity attributes (e.g., cannot complete an unfinished task); a feature flag or org setting that disables the transition's guard.
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
- transition_name: Transition is auto-triggered and cannot be
- transition_name: Unknown transition for this entity
- detail: {pydantic validation message}
- Transition validation failed for {self.transition_name}
- sample() does not accept arguments.
AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29).
Data as JSON: /api/errors/4a570c737d5cf4b9.
Report an issue: GitHub.