HumanSignal/label-studio · error · ValidationError
transition_name: Transition is auto-triggered and cannot be
Error message
transition_name: Transition is auto-triggered and cannot be executed manually
What it means
This 400 validation error is raised by FSMEntityTransitionAPI.post when the requested transition_name refers to a ModelChangeTransition subclass that has _triggers_on_create or _triggers_on_update set. Such transitions run automatically when the entity model is created or updated, so executing them through the manual transition endpoint would bypass (or duplicate) the automatic trigger. The FSM API refuses to invoke auto-triggered transitions by name via POST /api/fsm/entities/{entity_type}/{entity_id}/transition/.
Source
Thrown at label_studio/fsm/api.py:170
entity = self.get_entity()
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
transition_name = serializer.validated_data['transition_name']
transition_data = serializer.validated_data.get('transition_data') or {}
# Validate that transition is registered and manual (not auto-triggered)
transition_class = transition_registry.get_transition(entity_name, transition_name)
if not transition_class:
raise ValidationError({'transition_name': ['Unknown transition for this entity']})
# If it's a ModelChangeTransition and has any triggers configured, it's not manual
if issubclass(transition_class, ModelChangeTransition):
triggers_on_create = getattr(transition_class, '_triggers_on_create', False)
triggers_on_update = getattr(transition_class, '_triggers_on_update', False)
if triggers_on_create or triggers_on_update:
raise ValidationError(
{'transition_name': ['Transition is auto-triggered and cannot be executed manually']}
)
# 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 failureView on GitHub (pinned to 0b49e9b539)
Solutions
- Remove this transition_name from the manual API call; let the transition fire automatically on entity create/update instead.
- If a manual variant is needed, register a separate plain Transition (not a ModelChangeTransition with triggers) and call that name from the API.
- Check the transition class for _triggers_on_create/_triggers_on_update (set via trigger decorators/settings) and remove those triggers if the transition should truly be manual-only.
- Filter the registry listing used to build client UIs so auto-triggered transitions are not offered for manual execution.
Example fix
// before
POST /api/fsm/entities/task/123/transition/
{"transition_name": "task_updated_on_draft_unsaved"} // auto-triggered on update
// after
// don't call it manually; update the entity and let the trigger fire:
Task.objects.filter(id=123).update(...) // ModelChangeTransition fires automatically
// or register a manual-only transition and call it:
{"transition_name": "manually_approve_task"} Defensive patterns
Strategy: validation
Validate before calling
from fsm.registry import transition_registry
from fsm.transitions import ModelChangeTransition
tc = transition_registry.get_transition(entity_name, transition_name)
manual_ok = tc is not None and not (
issubclass(tc, ModelChangeTransition)
and (getattr(tc, '_triggers_on_create', False) or getattr(tc, '_triggers_on_update', False))
)
if not manual_ok:
raise ValueError(f'{transition_name} is not manually executable') Type guard
def is_manual_transition(transition_class) -> bool:
from fsm.transitions import ModelChangeTransition
if transition_class is None or not issubclass(transition_class, ModelChangeTransition):
return transition_class is not None
return not (getattr(transition_class, '_triggers_on_create', False) or getattr(transition_class, '_triggers_on_update', False)) Try / catch
from rest_framework.exceptions import ValidationError
try:
resp = client.post(f'/api/fsm/entities/{entity_type}/{entity_id}/transition/', payload)
except Exception as e:
if 'auto-triggered and cannot be executed manually' in str(e):
logger.info('transition %s is auto-triggered; skipping manual call', transition_name)
else:
raise Prevention
- List only manual transitions in UIs/clients by filtering out ModelChangeTransition subclasses with triggers
- Never call auto-triggered transitions by name from API clients
- Grep transition classes for _triggers_on_create/_triggers_on_update before wiring manual calls
When it happens
Trigger: POSTing to the FSM transition endpoint with a transition_name that resolves (via transition_registry.get_transition) to a ModelChangeTransition subclass whose _triggers_on_create or _triggers_on_update flag is True. Typically a client copies a transition name from the registry/code that is meant to fire from the model save path, not from the API.
Common situations: Automations that build transition calls from a registry listing all transitions without filtering auto-triggered ones; docs/SDK examples that show a transition name registered for model-save triggering; a transition class where someone later added on_create/on_update triggers, breaking previously-working manual API calls.
Related errors
- detail: {transition validation message}
- 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/d1c1f636caed23c0.
Report an issue: GitHub.