Flagsmith / Flagsmith/flagsmith

PATCHing a versioned segment override without feature_segment.id returns a 500

Open
#8,395 0 comments 0 reactions 0 assignees View on GitHub
api
Dominant language
Python
Stars
6.6k
Forks
567
Avg merge
1d 13h
Merged PRs (30d)
121

Description

Sentry Issue: [FLAGSMITH-API-6DZ](https://flagsmith.sentry.io/issues/7695361412/?referrer=github_integration)

```
UniqueViolation: duplicate key value violates unique constraint "features_featuresegment_feature_id_environment_i_f1fde686_uniq"
DETAIL: Key (feature_id, environment_id, segment_id, environment_feature_version_id)=(244763, 75194, 1157509, 374d8530-15d7-427b-97e8-8b41ad053f6b) already exists.

File "django/db/backends/utils.py", line 105, in _execute
return self.cursor.execute(sql, params)
File "/build/.venv/lib/python3.13/site-packages/opentelemetry/instrumentation/psycopg2/__init__.py", line 351, in execute
return _cursor_tracer.traced_execution(
File "/build/.venv/lib/python3.13/site-packages/opentelemetry/instrumentation/dbapi/__init__.py", line 761, in traced_execution
return query_method(*args, **kwargs)

IntegrityError: duplicate key value violates unique constraint "features_featuresegment_feature_id_environment_i_f1fde686_uniq"
DETAIL: Key (feature_id, environment_id, segment_id, environment_feature_version_id)=(244763, 75194, 1157509, 374d8530-15d7-427b-97e8-8b41ad053f6b) already exists.

(35 additional frame(s) were not displayed)
...
File "features/versioning/views.py", line 295, in perform_create
serializer.save(
File "features/versioning/serializers.py", line 41, in save
response = super().save(**kwargs) # type: ignore[no-untyped-call]
File "features/serializers.py", line 871, in create
return super().create(validated_data) # type: ignore[no-any-return,no-untyped-call]
File "features/feature_segments/serializers.py", line 80, in save
return super().save(**kwargs) # type: ignore[return-value]
```

Updating a segment override on a v2 feature version via `PATCH .../featurestates/{id}/` returns an unhandled 500 when the nested `feature_segment` payload omits `id`.

The cause is in how the nested serializer resolves the related object. `drf_writable_nested`'s `update_or_create_direct_relations` only looks the relation up by primary key taken from the request payload:

```python
pk = self._get_related_pk(data, model_class)
if pk:
obj = model_class.objects.filter(pk=pk).first()
```

With no `id` in the payload, `obj` is `None`, so `CustomCreateSegmentOverrideFeatureSegmentSerializer.save()` runs in create mode and inserts a second `FeatureSegment` for a `(feature, environment, segment, environment_feature_version)` combination that already exists.

### Steps To Reproduce

1. Enable v2 feature versioning on an environment and create a segment override for a feature.
2. Create a new feature version and note the feature state ID of the existing segment override.
3. `PATCH /api/v1/environments/{env}/features/{feature}/versions/{version_uuid}/featurestates/{fs_id}/` with body `{"feature_segment": {"priority": 0, "segment": }}` — i.e. without `feature_segment.id`.
4. Observe a 500 and the `IntegrityError` above.

### Expected behavior

The request should update the existing `FeatureSegment` in place (the combination of feature, environment, segment and version already identifies it uniquely), making the endpoint idempotent.

Failing that, it should at least be handled and returned as a 400 validation error rather than an unhandled 500.

Contributor guide

Open the contributing guide

Research direction

Start at features/versioning/views.py:295 and trace the PATCH request through features/versioning/serializers.py and features/feature_segments/serializers.py, focusing on how the nested feature_segment is resolved when id is absent. Reproduce the documented request against a versioned feature, then verify that the existing override is updated idempotently or that the conflict is returned as a 400 validation error instead of a 500.

Written by the indexing model from the issue text.

Assessment

Tech stack
django, python
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.