From 61f68ca9a6e5721c11cde2d272777044b43aa8bc Mon Sep 17 00:00:00 2001 From: wayneshn Date: Tue, 24 Feb 2026 12:15:49 +0100 Subject: [PATCH] fix(backend): improve ingestion error handling and error messages This commit introduces a "force delete" mechanism for Ingestion Sources and improves error messages for file-based connectors. Changes: - Update `IngestionService.delete` to accept a `force` flag, bypassing the `checkDeletionEnabled` check. - Use `force` deletion when rolling back failed ingestion source creations (e.g., decryption errors or connection failures) to ensure cleanup even if deletion is globally disabled. - Enhance error messages in `EMLConnector`, `MboxConnector`, and `PSTConnector` to distinguish between missing local files and failed uploads, providing more specific feedback to the user. --- packages/backend/src/services/IngestionService.ts | 15 +++++++++++---- .../services/ingestion-connectors/EMLConnector.ts | 15 ++++++++++++--- .../ingestion-connectors/MboxConnector.ts | 13 +++++++++++-- .../services/ingestion-connectors/PSTConnector.ts | 13 +++++++++++-- 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/services/IngestionService.ts b/packages/backend/src/services/IngestionService.ts index 00f83eb..681c80f 100644 --- a/packages/backend/src/services/IngestionService.ts +++ b/packages/backend/src/services/IngestionService.ts @@ -85,7 +85,7 @@ export class IngestionService { const decryptedSource = this.decryptSource(newSource); if (!decryptedSource) { - await this.delete(newSource.id, actor, actorIp); + await this.delete(newSource.id, actor, actorIp, true); throw new Error( 'Failed to process newly created ingestion source due to a decryption error.' ); @@ -107,7 +107,7 @@ export class IngestionService { } } catch (error) { // If connection fails, delete the newly created source and throw the error. - await this.delete(decryptedSource.id, actor, actorIp); + await this.delete(decryptedSource.id, actor, actorIp, true); throw error; } } @@ -205,8 +205,15 @@ export class IngestionService { return decryptedSource; } - public static async delete(id: string, actor: User, actorIp: string): Promise { - checkDeletionEnabled(); + public static async delete( + id: string, + actor: User, + actorIp: string, + force: boolean = false + ): Promise { + if (!force) { + checkDeletionEnabled(); + } const source = await this.findById(id); if (!source) { throw new Error('Ingestion source not found'); diff --git a/packages/backend/src/services/ingestion-connectors/EMLConnector.ts b/packages/backend/src/services/ingestion-connectors/EMLConnector.ts index 0aff453..1bc1520 100644 --- a/packages/backend/src/services/ingestion-connectors/EMLConnector.ts +++ b/packages/backend/src/services/ingestion-connectors/EMLConnector.ts @@ -58,7 +58,7 @@ export class EMLConnector implements IEmailConnector { try { const filePath = this.getFilePath(); if (!filePath) { - throw Error('EML file path not provided.'); + throw Error('EML Zip file path not provided.'); } if (!filePath.includes('.zip')) { throw Error('Provided file is not in the ZIP format.'); @@ -77,12 +77,21 @@ export class EMLConnector implements IEmailConnector { } if (!fileExist) { - throw Error('EML file not found or upload not finished yet, please wait.'); + if (this.credentials.localFilePath) { + throw Error(`EML Zip file not found at path: ${this.credentials.localFilePath}`); + } else { + throw Error( + 'Uploaded EML Zip file not found. The upload may not have finished yet, or it failed.' + ); + } } return true; } catch (error) { - logger.error({ error, credentials: this.credentials }, 'EML file validation failed.'); + logger.error( + { error, credentials: this.credentials }, + 'EML Zip file validation failed.' + ); throw error; } } diff --git a/packages/backend/src/services/ingestion-connectors/MboxConnector.ts b/packages/backend/src/services/ingestion-connectors/MboxConnector.ts index dab5eec..885a099 100644 --- a/packages/backend/src/services/ingestion-connectors/MboxConnector.ts +++ b/packages/backend/src/services/ingestion-connectors/MboxConnector.ts @@ -82,12 +82,21 @@ export class MboxConnector implements IEmailConnector { } if (!fileExist) { - throw Error('Mbox file not found or upload not finished yet, please wait.'); + if (this.credentials.localFilePath) { + throw Error(`Mbox file not found at path: ${this.credentials.localFilePath}`); + } else { + throw Error( + 'Uploaded Mbox file not found. The upload may not have finished yet, or it failed.' + ); + } } return true; } catch (error) { - logger.error({ error, credentials: this.credentials }, 'Mbox file validation failed.'); + logger.error( + { error, credentials: this.credentials }, + 'Mbox file validation failed.' + ); throw error; } } diff --git a/packages/backend/src/services/ingestion-connectors/PSTConnector.ts b/packages/backend/src/services/ingestion-connectors/PSTConnector.ts index d0f6171..f3b139a 100644 --- a/packages/backend/src/services/ingestion-connectors/PSTConnector.ts +++ b/packages/backend/src/services/ingestion-connectors/PSTConnector.ts @@ -161,11 +161,20 @@ export class PSTConnector implements IEmailConnector { } if (!fileExist) { - throw Error('PST file not found or upload not finished yet, please wait.'); + if (this.credentials.localFilePath) { + throw Error(`PST file not found at path: ${this.credentials.localFilePath}`); + } else { + throw Error( + 'Uploaded PST file not found. The upload may not have finished yet, or it failed.' + ); + } } return true; } catch (error) { - logger.error({ error, credentials: this.credentials }, 'PST file validation failed.'); + logger.error( + { error, credentials: this.credentials }, + 'PST file validation failed.' + ); throw error; } }