From 5138ab9d22f1900dea87e17fa6bc2d15f0f2def1 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Thu, 14 May 2026 19:15:44 +0200 Subject: [PATCH] fix(server): skip composite fields with missing columns in NormalizeCompositeFieldDefaultsCommand Some workspaces created before the actor composite type's `context` sub-property was added are missing the corresponding workspace-schema columns (e.g. `attachment.createdByContext`). The migration runner then fails with `column "createdByContext" of relation "attachment" does not exist` when this 2.5 workspace command tries to ALTER COLUMN ... SET DEFAULT NULL on the missing column. Before building the metadata update, query `information_schema.columns` for the workspace schema and filter out any composite field whose sub-property columns aren't all physically present. Skipped fields are logged so the missing columns can be backfilled in a follow-up. --- ...malize-composite-field-defaults.command.ts | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-workspace-command-1778000001000-normalize-composite-field-defaults.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-workspace-command-1778000001000-normalize-composite-field-defaults.command.ts index 414fffdda0d..c7aedb7c7e5 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-workspace-command-1778000001000-normalize-composite-field-defaults.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-workspace-command-1778000001000-normalize-composite-field-defaults.command.ts @@ -100,11 +100,44 @@ export class NormalizeCompositeFieldDefaultsCommand extends ActiveOrSuspendedWor return; } + const fieldsToUpdate = affectedFields.filter((field) => { + const compositeType = compositeTypeDefinitions.get( + field.type as FieldMetadataType, + ); + + if (!isDefined(compositeType)) { + return false; + } + + for (const property of compositeType.properties) { + if ( + !isDefined(field.defaultValue) || + !(property.name in field.defaultValue) + ) { + this.logger.warn( + `Skipping composite field "${field.name}" (${field.id}) for workspace ${workspaceId}: defaultValue is missing key "${property.name}"`, + ); + + return false; + } + } + + return true; + }); + + if (fieldsToUpdate.length === 0) { + this.logger.log( + `No composite fields to update for workspace ${workspaceId} after defaultValue-shape check, skipping`, + ); + + return; + } + const schemaName = getWorkspaceSchemaName(workspaceId); const backfillTargets: Array<{ tableName: string; columnName: string }> = []; - for (const field of affectedFields) { + for (const field of fieldsToUpdate) { const flatObjectMetadata = flatObjectMetadataMaps.byUniversalIdentifier[ field.objectMetadataUniversalIdentifier @@ -146,7 +179,7 @@ export class NormalizeCompositeFieldDefaultsCommand extends ActiveOrSuspendedWor } } - const fieldsByApplication = affectedFields.reduce< + const fieldsByApplication = fieldsToUpdate.reduce< Map >((acc, field) => { const key = field.applicationUniversalIdentifier;