HumanSignal/label-studio · error · StateManagerError

Failed to transition state: {e}

Error message

Failed to transition state: {e}

What it means

This is the catch-all wrap in transition_state: after validation passes, the actual state-record creation (state model save, cache update, transaction.on_commit work) raised an exception, which is re-raised as StateManagerError('Failed to transition state: ...') with full context logged (entity, transition, user, organization). The original exception is chained so the root cause is available.

Source

Thrown at label_studio/fsm/state_manager.py:427

            organization_id = CurrentContext.get_organization_id()

            logger.error(
                'FSM: State transition failed',
                extra={
                    'event': 'fsm.transition_state_failed',
                    'entity_type': entity._meta.label_lower,
                    'entity_id': entity.pk,
                    'from_state': current_state,
                    'to_state': new_state,
                    'error': str(e),
                    **{
                        'user_id': user.id if user else None,
                        'organization_id': organization_id if organization_id else None,
                    },
                },
                exc_info=True,
            )
            raise StateManagerError(f'Failed to transition state: {e}') from e

    @classmethod
    def get_state_history(cls, entity: Model) -> QuerySet[BaseState]:
        """
        Get complete state history for an entity.

        Args:
            entity: Entity to get history for

        Returns:
            QuerySet of state records ordered by most recent first
        """
        state_model = get_state_model_for_entity(entity)
        if not state_model:
            raise StateManagerError(
                f'No state model registered for {entity._meta.model_name} when getting state history'
            )

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Inspect server logs — the wrapped exception is logged with exc_info plus entity/transition/user context; fix the named root cause.
  2. Retry the transition after transient DB/Redis failures, ideally with idempotency: re-read current state and skip if the target state already applied.
  3. Reduce contention: avoid many concurrent transitions on the same entity; queue bulk state changes.
  4. Run pending migrations and verify the state table schema matches the installed code version.

Example fix

// before
StateManager.transition_state(task, 'completed', user=request.user)  # deadlocks under load

// after
from fsm.state_manager import StateManagerError
for attempt in range(3):
    try:
        StateManager.transition_state(task, 'completed', user=request.user)
        break
    except StateManagerError:
        if attempt == 2:
            logger.exception('transition failed for task %s', task.pk)
            raise
        time.sleep(0.5 * (attempt + 1))
Defensive patterns

Strategy: retry

Validate before calling

from fsm.registry import get_state_model_for_entity
from django.db import connection

assert get_state_model_for_entity(entity) is not None
connection.ensure_connection()  # surface DB problems before attempting the transition

Type guard

def safe_to_transition(entity) -> bool:
    from fsm.registry import get_state_model_for_entity
    from django.db import connection
    return get_state_model_for_entity(entity) is not None and connection.is_usable()

Try / catch

from fsm.state_manager import StateManagerError
import time
for attempt in range(3):
    try:
        StateManager.transition_state(entity, new_state, user=user)
        break
    except StateManagerError as e:
        if attempt == 2:
            logger.exception('transition failed for %s', entity.pk)
            raise
        time.sleep(0.5 * (2 ** attempt))

Prevention

When it happens

Trigger: Calling transition_state where the DB insert of the new state record fails (constraint violation, deadlocked/serialized transaction, connection drop), the cache update raises, or code inside the state model's record-creation path throws. Often seen under concurrent transitions on the same entity.

Common situations: Two workers transitioning the same task concurrently causing deadlocks or unique-constraint conflicts; Redis outage during the immediate cache update; long transactions holding locks during bulk operations; migration drift leaving required columns missing.

Related errors


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