From cd5aca0f2154ddd52fa81487db5b5aa96c130d83 Mon Sep 17 00:00:00 2001 From: Deep Santoshwar Date: Sat, 1 Aug 2026 00:59:11 +0530 Subject: [PATCH] fix(api): update-traits returns 400 for malformed request body Sending a non-object payload (e.g. a JSON array) to the edge identity update-traits endpoint unpacked request.data directly into TraitModel, raising an uncaught TypeError and a 500 instead of a validation error. --- api/edge_api/identities/views.py | 2 ++ .../identities/test_edge_identity_viewset.py | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/api/edge_api/identities/views.py b/api/edge_api/identities/views.py index ab241b6ba652..f3812d037b59 100644 --- a/api/edge_api/identities/views.py +++ b/api/edge_api/identities/views.py @@ -176,6 +176,8 @@ def update_traits(self, request, *args, **kwargs): # type: ignore[no-untyped-de if not environment.project.organisation.persist_trait_data: raise TraitPersistenceError() edge_identity = self.get_object() + if not isinstance(request.data, dict): + raise ValidationError({"detail": "Request data must be a JSON object."}) try: trait = TraitModel(**request.data) except pydantic.ValidationError as validation_error: diff --git a/api/tests/integration/edge_api/identities/test_edge_identity_viewset.py b/api/tests/integration/edge_api/identities/test_edge_identity_viewset.py index 9d16e4e3193e..c16de07d4204 100644 --- a/api/tests/integration/edge_api/identities/test_edge_identity_viewset.py +++ b/api/tests/integration/edge_api/identities/test_edge_identity_viewset.py @@ -696,3 +696,30 @@ def test_edge_identities_update_trait__persist_trait_data_disabled__returns_400( ) # Then assert response.status_code == status.HTTP_400_BAD_REQUEST + + +def test_edge_identities_update_trait__malformed_payload__returns_400( # type: ignore[no-untyped-def] + admin_client, + dynamo_enabled_environment, + environment_api_key, + identity_document, + edge_identity_dynamo_wrapper_mock, +): + # Given + edge_identity_dynamo_wrapper_mock.get_item_from_uuid_or_404.return_value = ( + identity_document + ) + identity_uuid = identity_document["identity_uuid"] + url = reverse( + "api-v1:environments:environment-edge-identities-update-traits", + args=[environment_api_key, identity_uuid], + ) + data = [{"trait_key": "some_trait_key", "trait_value": "some_trait_value"}] + + # When + response = admin_client.put( + url, data=json.dumps(data), content_type="application/json" + ) + + # Then + assert response.status_code == status.HTTP_400_BAD_REQUEST