HumanSignal/label-studio · error · NotImplementedError
FSMStateField is read-only. Use transitions to change state.
Error message
FSMStateField is read-only. Use transitions to change state.
What it means
FSMStateField is declared read-only for DRF serializers; to_internal_value raises NotImplementedError because incoming state values must never be written directly through a serializer. Entity state is an internal concept managed exclusively by the StateManager's transitions, which create INSERT-only audit records. Attempting to bind or deserialize client data into this field hits this hard stop.
Source
Thrown at label_studio/fsm/serializer_fields.py:125
return instance.state
elif hasattr(instance, 'current_state'):
# Fallback to current_state annotation from FSMStateQuerySetMixin
return instance.current_state
# Fallback: Query the state manager
# This happens when the queryset wasn't annotated
# StateManager has its own caching, so this is still efficient
try:
return StateManager.get_current_state_value(instance)
except Exception:
# If FSM is disabled or state model not found, return None
return None
def to_internal_value(self, data):
"""
This field is read-only, so this should never be called.
"""
raise NotImplementedError('FSMStateField is read-only. Use transitions to change state.')
View on GitHub (pinned to 0b49e9b539)
Solutions
- Mark the field read-only in your serializer: state = FSMStateField(read_only=True) or add 'state' to Meta.read_only_fields.
- Change entity state by invoking a registered transition (StateManager.execute_transition or the transition API) instead of PATCHing the field.
- Filter incoming payloads to exclude 'state' before passing request.data into the serializer.
Example fix
// before
class TaskSerializer(serializers.ModelSerializer):
state = FSMStateField()
class Meta:
fields = '__all__'
// after
class TaskSerializer(serializers.ModelSerializer):
state = FSMStateField(read_only=True)
class Meta:
fields = '__all__'
read_only_fields = ('state',) Defensive patterns
Strategy: validation
Validate before calling
data = request.data
if 'state' in data:
return Response({'state': 'Read-only; use transitions to change state.'}, status=400)
serializer = MyEntitySerializer(data=data) Type guard
def payload_has_no_state_field(data: dict) -> bool:
return 'state' not in data Try / catch
try:
serializer = MyEntitySerializer(instance, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
except NotImplementedError as e:
if 'FSMStateField is read-only' in str(e):
return Response({'detail': 'Use a state transition to change state.'}, status=400)
raise Prevention
- Declare FSMStateField as read_only in every serializer that exposes it
- Never include internal state columns in writable serializer fields
- Change entity state only via registered transitions, never via PATCH/PUT
When it happens
Trigger: Including 'state' (the FSMStateField column) in writable serializer fields and submitting it via PATCH/POST; constructing a serializer with data= containing the state key; generic model-crud code that iterates model fields and builds a serializer accepting state input.
Common situations: Copy-pasting an entity serializer and forgetting to declare state read-only; a bulk import/update endpoint that echoes all model fields as writable; frontend form code that round-trips the full serialized entity back to the server including the state value.
Related errors
- Invalid entity name: {entity_name}
- transition_name: Unknown transition for this entity
- No state model found for {entity._meta.model_name} when gett
- No state model found for {entity._meta.model_name} when gett
- No state model found for {entity._meta.model_name} when tran
AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29).
Data as JSON: /api/errors/6414eb1951fc91fb.
Report an issue: GitHub.