HumanSignal/label-studio · error · StateManagerError

No state model found for {entity._meta.model_name} when tran

Error message

No state model found for {entity._meta.model_name} when transitioning state

What it means

transition_state first checks whether FSM is disabled via CurrentContext.is_fsm_disabled() (silently succeeding), then resolves the entity's state model; if the registry has none, it raises StateManagerError noting the model can't be transitioned. Transitions can only be recorded through a registered state model, so transitioning an unregistered entity type is refused rather than silently ignored.

Source

Thrown at label_studio/fsm/state_manager.py:271

                entity=task,
                new_state='IN_PROGRESS',
                transition_name='start_annotation',
                user=request.user,
                organization_id=request.user.active_organization_id,
                context={'assignment_id': assignment.id},
                reason='User started annotation work'
            )
        """
        if not cls._is_fsm_enabled(user=user):
            return True  # Feature disabled, silently succeed

        # Skip if FSM is temporarily disabled (e.g., during cleanup or bulk operations)
        if CurrentContext.is_fsm_disabled():
            return True  # FSM disabled, silently succeed

        state_model = get_state_model_for_entity(entity)
        if not state_model:
            raise StateManagerError(f'No state model found for {entity._meta.model_name} when transitioning state')

        current_state = cls.get_current_state_value(entity)

        # Prevent same-state transitions - only create state records for actual state changes
        # This avoids creating redundant data when the effective state doesn't change
        # However, allow forced state records for audit trails (e.g., annotation updates)
        # IMPORTANT: Also check if a state record exists in DB - if not, we must create one
        # even if inferred state matches target state (to persist the inferred state)
        if current_state == new_state and not force_state_record:
            # Verify a state record actually exists in DB (not just inferred)
            state_record_exists = state_model.objects.filter(**{entity._meta.model_name: entity}).exists()
            if state_record_exists:
                return True  # Skip transition - record exists and state unchanged
            # else: No record exists (state was inferred), continue to create record

        # Optimistic concurrency control using cache-based locking
        cache_key = cls.get_cache_key(entity)
        lock_key = f'{cache_key}:lock'

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Register a state model for the entity, or route the change through a registered transition instead of raw transition_state.
  2. Guard the call: only invoke transition_state when get_state_model_for_entity(entity) returns a model.
  3. Audit signal handlers/bulk-update code so they filter to FSM-managed models before calling transition_state.
  4. If it should be a silent no-op, use the CurrentContext FSM-disable mechanism (is_fsm_disabled path) around non-FSM bulk operations.

Example fix

// before
@receiver(post_save, sender=MyModel)
def on_save(sender, instance, **kwargs):
    StateManager.transition_state(instance, 'updated')  # raises for unregistered model

// after
@receiver(post_save, sender=MyModel)
def on_save(sender, instance, **kwargs):
    if get_state_model_for_entity(instance):
        StateManager.transition_state(instance, 'updated')
Defensive patterns

Strategy: validation

Validate before calling

from fsm.registry import get_state_model_for_entity
from core.current_request import CurrentContext

if CurrentContext.is_fsm_disabled() or get_state_model_for_entity(entity) is None:
    return True  # nothing to transition; mirror the manager's silent-skip path

Type guard

def can_transition(entity) -> bool:
    from fsm.registry import get_state_model_for_entity
    return get_state_model_for_entity(entity) is not None

Try / catch

from fsm.state_manager import StateManager, StateManagerError
try:
    StateManager.transition_state(entity, new_state, user=user)
except StateManagerError as e:
    if 'No state model found' in str(e):
        logger.info('skipping transition for unregistered model %s', entity._meta.model_name)
    else:
        raise

Prevention

When it happens

Trigger: Calling StateManager.transition_state(entity, new_state, ...) with an entity whose model is absent from the state-model registry — custom code invoking transitions directly, signal handlers on models without FSM registration, or post-save hooks firing for entity types never onboarded to the FSM.

Common situations: Adding a signal handler that transitions state for every save and then firing it for a non-FSM model; renaming a model so the registry key no longer matches; running custom scripts/management commands against models registered only in the enterprise edition.

Related errors


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