fix(workflow_engine): Sanitize corrupted dynamic_form_fields choice labels#115855
Conversation
…abels
A legacy code path (ensureCurrentOption in AbstractExternalIssueForm) could
persist React element labels into choice tuples stored in Action.data. After
JSON round-tripping these become {key, ref, props} objects that crash on
render. Add a post-deployment data migration that iterates ticket-type actions,
extracts visible text from the serialized React elements, and replaces the
corrupted labels with plain strings.
fab5775 to
ad837f8
Compare
|
This PR has a migration; here is the generated SQL for for --
-- Raw Python operation
--
-- THIS OPERATION CANNOT BE WRITTEN AS SQL |
| row_changed = True | ||
|
|
||
| if row_changed: | ||
| action.save(update_fields=["data"]) |
There was a problem hiding this comment.
do we need to modify the json schema for the data field?
There was a problem hiding this comment.
The schema only defines dynamic_form_fields as an array of objects:
| "dynamic_form_fields": [ | ||
| { | ||
| "name": "assignee", | ||
| "choices": [["user-1", "Alice"], ["user-2", "Bob"]], | ||
| } | ||
| ], |
There was a problem hiding this comment.
is this data replicated on each action? 🤔 i wonder if it'd be better to have it normalized and shared. probably too big of a lift for now, but food for thought.
There was a problem hiding this comment.
Yeah this is probably something we could do? I'm not too well versed on dynamic_form_fields
There was a problem hiding this comment.
sg - can think of it as a follow-up / cleanup thing if we can get to it.
| for action in RangeQuerySetWrapper(Action.objects.filter(type__in=TICKET_ACTION_TYPES)): | ||
| count += 1 |
There was a problem hiding this comment.
| for action in RangeQuerySetWrapper(Action.objects.filter(type__in=TICKET_ACTION_TYPES)): | |
| count += 1 | |
| for count, action in enumerate(RangeQuerySetWrapper(Action.objects.filter(type__in=TICKET_ACTION_TYPES))): |
i'm not sure if enumerate does something to RangeQuerySetWrapper or not, but you can use enumerate like this to also get the count, so you don't have to track it.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2192d98. Configure here.
…abels (#115855) A legacy code path (in the old `AbstractExternalIssueForm`) could persist React element labels (like `<QuestionTooltip />`) into choice tuples stored in `Action.data.dynamic_form_fields`. After JSON round-tripping, these become plain `{key, ref, props}` objects which, now with the new form components, crash on render. We never should have been saving these react components to the database, so this PR aims to fix that bad data. This is a post-deploy migration which goes over all ticketing actions and fixes any which have objects saved as the label. See here for a full list of bad actions in the DB: https://redash.getsentry.net/queries/11274/source The serialized react elements all look like this, so we can easily extract the actual label: ``` { "key": None, "ref": None, "props": { "children": [ {...}, " ", "This is the actual label text", ] } } ```
…abels (#115855) A legacy code path (in the old `AbstractExternalIssueForm`) could persist React element labels (like `<QuestionTooltip />`) into choice tuples stored in `Action.data.dynamic_form_fields`. After JSON round-tripping, these become plain `{key, ref, props}` objects which, now with the new form components, crash on render. We never should have been saving these react components to the database, so this PR aims to fix that bad data. This is a post-deploy migration which goes over all ticketing actions and fixes any which have objects saved as the label. See here for a full list of bad actions in the DB: https://redash.getsentry.net/queries/11274/source The serialized react elements all look like this, so we can easily extract the actual label: ``` { "key": None, "ref": None, "props": { "children": [ {...}, " ", "This is the actual label text", ] } } ```
One assert per test gives clearer failure output than the single multi-block test_migration. Also drops a stale `self.apps_before` reference that would have AttributeError'd if the class were ever unskipped — use the live Rule model in setup, matching #115855's style. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Fixes https://linear.app/getsentry/issue/ISWF-2670/alerts-form-crashes-when-opening-ticket-action-details-modal-in-some
A legacy code path (in the old
AbstractExternalIssueForm) could persist React element labels (like<QuestionTooltip />) into choice tuples stored inAction.data.dynamic_form_fields. After JSON round-tripping, these become plain{key, ref, props}objects which, now with the new form components, crash on render. We never should have been saving these react components to the database, so this PR aims to fix that bad data.This is a post-deploy migration which goes over all ticketing actions and fixes any which have objects saved as the label.
See here for a full list of bad actions in the DB: https://redash.getsentry.net/queries/11274/source
The serialized react elements all look like this, so we can easily extract the actual label: