{"record":{"id":"6414eb1951fc91fb","repo":"HumanSignal/label-studio","slug":"fsmstatefield-is-read-only-use-transitions-to-cha","errorCode":null,"errorMessage":"FSMStateField is read-only. Use transitions to change state.","messagePattern":"FSMStateField is read-only\\. Use transitions to change state\\.","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"label_studio/fsm/serializer_fields.py","lineNumber":125,"sourceCode":"            return instance.state\n        elif hasattr(instance, 'current_state'):\n            # Fallback to current_state annotation from FSMStateQuerySetMixin\n            return instance.current_state\n\n        # Fallback: Query the state manager\n        # This happens when the queryset wasn't annotated\n        # StateManager has its own caching, so this is still efficient\n        try:\n            return StateManager.get_current_state_value(instance)\n        except Exception:\n            # If FSM is disabled or state model not found, return None\n            return None\n\n    def to_internal_value(self, data):\n        \"\"\"\n        This field is read-only, so this should never be called.\n        \"\"\"\n        raise NotImplementedError('FSMStateField is read-only. Use transitions to change state.')\n","sourceCodeStart":107,"sourceCodeEnd":126,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/fsm/serializer_fields.py#L107-L126","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nclass TaskSerializer(serializers.ModelSerializer):\n    state = FSMStateField()\n    class Meta:\n        fields = '__all__'\n\n// after\nclass TaskSerializer(serializers.ModelSerializer):\n    state = FSMStateField(read_only=True)\n    class Meta:\n        fields = '__all__'\n        read_only_fields = ('state',)","handlingStrategy":"validation","validationCode":"data = request.data\nif 'state' in data:\n    return Response({'state': 'Read-only; use transitions to change state.'}, status=400)\nserializer = MyEntitySerializer(data=data)","typeGuard":"def payload_has_no_state_field(data: dict) -> bool:\n    return 'state' not in data","tryCatchPattern":"try:\n    serializer = MyEntitySerializer(instance, data=request.data)\n    serializer.is_valid(raise_exception=True)\n    serializer.save()\nexcept NotImplementedError as e:\n    if 'FSMStateField is read-only' in str(e):\n        return Response({'detail': 'Use a state transition to change state.'}, status=400)\n    raise","preventionTips":["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"],"tags":["serializer","django","drf","read-only","fsm"],"backgroundTag":"read-only-field-write-attempt","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}