Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 6743ee194f Workflow step deletion fails when referenced logic function already deleted
https://sonarly.com/issue/5446?type=bug

Deleting a CODE workflow step throws "Logic function to destroy not found" when the referenced logic function has already been destroyed, causing the deletion to appear failed even though the step was successfully removed from the workflow version.

Fix: The fix makes the CODE step deletion side effect idempotent by catching `LogicFunctionException` with code `LOGIC_FUNCTION_NOT_FOUND` and silently ignoring it. All other exception types are re-thrown, so genuine errors are not suppressed.

The key insight is that "logic function already deleted" is a valid idempotent outcome for a cleanup side effect — especially since the workflow version step is already removed from the DB before side effects run. Treating a missing resource as a fatal error in a cleanup path is incorrect; the desired end state (logic function gone) has already been achieved.

```typescript file=packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-operations.workspace-service.ts lines=24-27
import {
  LogicFunctionException,
  LogicFunctionExceptionCode,
} from 'src/engine/metadata-modules/logic-function/logic-function.exception';
```

```typescript file=packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-operations.workspace-service.ts lines=93-110
case WorkflowActionType.CODE: {
  try {
    await this.logicFunctionFromSourceService.deleteOneWithSource({
      id: step.settings.input.logicFunctionId,
      workspaceId,
    });
  } catch (error) {
    if (
      !(
        error instanceof LogicFunctionException &&
        error.code ===
          LogicFunctionExceptionCode.LOGIC_FUNCTION_NOT_FOUND
      )
    ) {
      throw error;
    }
  }
  break;
}
```
2026-03-03 05:54:25 +00:00
@@ -21,6 +21,10 @@ import { AiAgentRoleService } from 'src/engine/metadata-modules/ai/ai-agent-role
import { AgentService } from 'src/engine/metadata-modules/ai/ai-agent/agent.service';
import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import {
LogicFunctionException,
LogicFunctionExceptionCode,
} from 'src/engine/metadata-modules/logic-function/logic-function.exception';
import { LogicFunctionFromSourceService } from 'src/engine/metadata-modules/logic-function/services/logic-function-from-source.service';
import { findFlatLogicFunctionOrThrow } from 'src/engine/metadata-modules/logic-function/utils/find-flat-logic-function-or-throw.util';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
@@ -87,10 +91,22 @@ export class WorkflowVersionStepOperationsWorkspaceService {
}) {
switch (step.type) {
case WorkflowActionType.CODE: {
await this.logicFunctionFromSourceService.deleteOneWithSource({
id: step.settings.input.logicFunctionId,
workspaceId,
});
try {
await this.logicFunctionFromSourceService.deleteOneWithSource({
id: step.settings.input.logicFunctionId,
workspaceId,
});
} catch (error) {
if (
!(
error instanceof LogicFunctionException &&
error.code ===
LogicFunctionExceptionCode.LOGIC_FUNCTION_NOT_FOUND
)
) {
throw error;
}
}
break;
}
case WorkflowActionType.AI_AGENT: {