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

  1. Fetch the entity's current state (GET state history or state endpoint) and only submit transitions valid from that state.
  2. Read the returned detail message — it comes from the transition's own validation and names the unmet rule.
  3. Handle concurrent updates: re-fetch the entity and retry with a transition valid from the new state.
  4. 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

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


AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29). Data as JSON: /api/errors/4a570c737d5cf4b9. Report an issue: GitHub.