From 546ab0a036c86ba624089bfac5fdea0310751d8c Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Fri, 8 May 2026 09:05:22 +0200 Subject: [PATCH] fix: handle widgets with missing universalConfiguration in 2.3 delete-gauge-widgets command (#20393) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary The 2.3 `upgrade:2-3:delete-gauge-widgets` workspace command crashed in production for ~10 workspaces (out of 5000) with: ``` [Error] Cannot read properties of undefined (reading 'configurationType') at .../2-3-workspace-command-1798000000000-delete-gauge-widgets.command.js:35:164 at Array.filter () ``` ### Root cause Those workspaces have legacy `pageLayoutWidget` rows whose `configuration` JSONB does not contain a recognized `configurationType`. This is consistent with the 1.15 backfill (`MigratePageLayoutWidgetConfigurationCommand`) only migrating widgets with the deprecated `graphType` and the `IFRAME` / `STANDALONE_RICH_TEXT` types — any other widget type that was already missing `configurationType` (or has a value not in the current enum) was left as-is. When the cache is recomputed, `fromPageLayoutWidgetConfigurationToUniversalConfiguration` switches on `configuration.configurationType`. With no matching case, the function falls through and returns `undefined`, so the cached `widget.universalConfiguration` ends up `undefined`. The gauge filter then dereferences `.configurationType` and throws. We can't reproduce the affected data locally, but the symptom uniquely points at this fall-through path — every other code path either throws earlier (e.g. when `configuration` itself is null) or yields a defined `universalConfiguration`. ### Fix In `2-3-workspace-command-1798000000000-delete-gauge-widgets.command.ts`: - Skip widgets whose `universalConfiguration` is `undefined` — by definition they aren't gauge widgets, so they don't belong in the deletion set. - Log them as a warning (id and count) so we still have visibility on the corrupt rows for follow-up cleanup. - Use optional chaining when comparing the configuration type so the filter is robust to the same shape going forward. The fix is minimal and additive: workspaces without corrupt widgets behave exactly as before, and the upgrade can now succeed on the affected workspaces. ## Test plan - [ ] CI lint + typecheck green - [ ] Run the upgrade on a healthy workspace locally — gauge widgets are still deleted, no warnings logged - [ ] On production, verify the 2.3 upgrade no longer fails on the affected ~10 workspaces and that the warning logs surface the offending widget ids for follow-up ## Follow-ups (out of scope of this PR) - Investigate the corrupt widgets surfaced by the new warning log and decide whether to backfill / delete them in a dedicated upgrade command - Consider hardening `fromPageLayoutWidgetConfigurationToUniversalConfiguration` so the switch fall-through fails loudly (or returns a sentinel) instead of silently yielding `undefined` --- ...8000000000-delete-gauge-widgets.command.ts | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-3/2-3-workspace-command-1798000000000-delete-gauge-widgets.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-3/2-3-workspace-command-1798000000000-delete-gauge-widgets.command.ts index 56b4aebce2..3956305314 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-3/2-3-workspace-command-1798000000000-delete-gauge-widgets.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-3/2-3-workspace-command-1798000000000-delete-gauge-widgets.command.ts @@ -37,15 +37,31 @@ export class DeleteGaugeWidgetsCommand extends ActiveOrSuspendedWorkspaceCommand 'flatPageLayoutWidgetMaps', ]); - const gaugeWidgets = Object.values( + // Some legacy widgets have configurations with no recognized configurationType + // (e.g., not backfilled by the 1.15 widget configuration migration), which + // makes universalConfiguration undefined after cache recomputation. Skip them + // since they cannot be gauge widgets, but log them for visibility. + const widgets = Object.values( flatPageLayoutWidgetMaps.byUniversalIdentifier, - ) - .filter(isDefined) - .filter( - (widget) => - widget.universalConfiguration.configurationType === - WidgetConfigurationType.GAUGE_CHART, + ).filter(isDefined); + + const widgetsWithMissingUniversalConfiguration = widgets.filter( + (widget) => !isDefined(widget.universalConfiguration), + ); + + if (widgetsWithMissingUniversalConfiguration.length > 0) { + this.logger.warn( + `Found ${widgetsWithMissingUniversalConfiguration.length} widget(s) with missing universalConfiguration in workspace ${workspaceId}, skipping them: ${widgetsWithMissingUniversalConfiguration + .map((widget) => widget.id) + .join(', ')}`, ); + } + + const gaugeWidgets = widgets.filter( + (widget) => + widget.universalConfiguration?.configurationType === + WidgetConfigurationType.GAUGE_CHART, + ); if (gaugeWidgets.length === 0) { this.logger.log(`No gauge widgets in workspace ${workspaceId}`);