From fb5a1988b1d77f037b748c2b33bc158920148907 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Fri, 17 Apr 2026 22:31:35 +0200 Subject: [PATCH] fix(server): log inner errors of WorkspaceMigrationRunnerException in workspace iterator (#19823) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary When a workspace migration action fails during workspace iteration (e.g. during upgrade commands), only the wrapper message was logged: ``` [WorkspaceIteratorService] Error in workspace 7914ba64-...: Migration action 'create' for 'pageLayoutWidget' failed ``` The underlying error (transpilation/metadata/workspace schema) and its stack were swallowed, making production debugging painful. This PR adds a follow-up log entry for each inner error attached to a `WorkspaceMigrationRunnerException`, including its message and stack trace. The runner exception itself is untouched — it already exposes structured `errors` (`actionTranspilation`, `metadata`, `workspaceSchema`). After this change, logs look like: ``` [WorkspaceIteratorService] Error in workspace 7914ba64-...: Migration action 'create' for 'pageLayoutWidget' failed [WorkspaceIteratorService] Caused by actionTranspilation in workspace 7914ba64-...: at ... ``` ## Test plan - [ ] Trigger a failing workspace migration (e.g. backfill record page layouts) on a workspace and confirm the underlying cause + stack now appear in logs. Made with [Cursor](https://cursor.com) --- .../workspace-iterator.service.ts | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/twenty-server/src/database/commands/command-runners/workspace-iterator.service.ts b/packages/twenty-server/src/database/commands/command-runners/workspace-iterator.service.ts index e4d51ea07b..ebd92496c5 100644 --- a/packages/twenty-server/src/database/commands/command-runners/workspace-iterator.service.ts +++ b/packages/twenty-server/src/database/commands/command-runners/workspace-iterator.service.ts @@ -4,12 +4,14 @@ import { InjectRepository } from '@nestjs/typeorm'; import chalk from 'chalk'; import { isNonEmptyString } from '@sniptt/guards'; import { WorkspaceActivationStatus } from 'twenty-shared/workspace'; +import { isDefined } from 'twenty-shared/utils'; import { In, MoreThanOrEqual, Repository } from 'typeorm'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { GlobalWorkspaceDataSource } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; +import { WorkspaceMigrationRunnerException } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/exceptions/workspace-migration-runner.exception'; export type WorkspaceIteratorArgs = { workspaceIds?: string[]; @@ -104,12 +106,29 @@ export class WorkspaceIteratorService { } } - report.fail.forEach(({ error, workspaceId }) => + report.fail.forEach(({ error, workspaceId }) => { this.logger.error( `Error in workspace ${workspaceId}: ${error.message}`, error.stack, - ), - ); + ); + + if (error instanceof WorkspaceMigrationRunnerException && error.errors) { + for (const [label, innerError] of Object.entries(error.errors)) { + if (!isDefined(innerError)) continue; + + if (innerError instanceof Error) { + this.logger.error( + `Caused by ${label} in workspace ${workspaceId}: ${innerError.message}`, + innerError.stack, + ); + } else { + this.logger.error( + `Caused by ${label} in workspace ${workspaceId}: ${String(innerError)}`, + ); + } + } + } + }); return report; }