From a730cf3f7d019ba09098ca29c7d627947348b970 Mon Sep 17 00:00:00 2001 From: Sonarly Claude Code Date: Thu, 16 Apr 2026 03:52:20 +0000 Subject: [PATCH] Logic function FILE_NOT_FOUND: missing package.json check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://sonarly.com/issue/26891?type=bug Logic function execution fails with FileStorageException FILE_NOT_FOUND because copyDependenciesInMemory unconditionally downloads package.json from S3 without checking if it exists first, unlike yarn.lock which has a fallback. Dependency files are only uploaded during new workspace creation, so upgraded workspaces lack them entirely. Fix: Added a checkFileExists guard for package.json in copyDependenciesInMemory, mirroring the existing yarn.lock pattern from commit f3e0c12ce6a. Before this fix, copyDependenciesInMemory unconditionally called fileStorageService.downloadFile for package.json without checking if it exists in S3. If the file was missing (e.g., workspace upgraded from pre-application-system version, or S3 upload failed during workspace creation), the S3 driver threw FileStorageException(FILE_NOT_FOUND) which killed logic function execution. The fix: 1. Both checkFileExists calls (package.json and yarn.lock) now run in parallel via Promise.all for efficiency 2. If package.json exists in S3, it is downloaded as before 3. If package.json is missing, the default seed package.json from SEED_DEPENDENCIES_DIRNAME is copied to the in-memory folder using fs.copyFile — this provides the full default dependency set so logic functions can still execute 4. Added import for SEED_DEPENDENCIES_DIRNAME constant and path module This is the exact same defensive pattern already used for yarn.lock, which was added in commit f3e0c12ce6a but was not applied to package.json at that time. --- .../logic-function-resource.service.ts | 50 +++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.service.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.service.ts index ec8eb90fd76..5e9b736309b 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.service.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import crypto from 'crypto'; import { promises as fs } from 'fs'; -import { dirname, join } from 'path'; +import path, { dirname, join } from 'path'; import { type QueryRunner } from 'typeorm'; import { FileFolder } from 'twenty-shared/types'; @@ -10,6 +10,7 @@ import { isDefined } from 'twenty-shared/utils'; import { FileStorageExceptionCode } from 'src/engine/core-modules/file-storage/interfaces/file-storage-exception'; +import { SEED_DEPENDENCIES_DIRNAME } from 'src/engine/core-modules/application/application-package/constants/seed-dependencies-dirname'; import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service'; import { getLogicFunctionSeedProjectFiles, @@ -257,24 +258,45 @@ export class LogicFunctionResourceService { workspaceId: string; inMemoryFolderPath: string; }) { - const yarnLockExists = await this.fileStorageService.checkFileExists({ - workspaceId, - applicationUniversalIdentifier, - fileFolder: FileFolder.Dependencies, - resourcePath: 'yarn.lock', - }); - - const promises = []; - - promises.push( - this.fileStorageService.downloadFile({ + const [packageJsonExists, yarnLockExists] = await Promise.all([ + this.fileStorageService.checkFileExists({ workspaceId, applicationUniversalIdentifier, fileFolder: FileFolder.Dependencies, resourcePath: 'package.json', - localPath: join(inMemoryFolderPath, 'package.json'), }), - ); + this.fileStorageService.checkFileExists({ + workspaceId, + applicationUniversalIdentifier, + fileFolder: FileFolder.Dependencies, + resourcePath: 'yarn.lock', + }), + ]); + + const promises = []; + + if (packageJsonExists) { + promises.push( + this.fileStorageService.downloadFile({ + workspaceId, + applicationUniversalIdentifier, + fileFolder: FileFolder.Dependencies, + resourcePath: 'package.json', + localPath: join(inMemoryFolderPath, 'package.json'), + }), + ); + } else { + const packageJsonPath = join(inMemoryFolderPath, 'package.json'); + + promises.push( + fs.mkdir(dirname(packageJsonPath), { recursive: true }).then(() => + fs.copyFile( + path.join(SEED_DEPENDENCIES_DIRNAME, 'package.json'), + packageJsonPath, + ), + ), + ); + } if (yarnLockExists) { promises.push(