diff --git a/apps/sim/lib/workflows/executor/execution-core.test.ts b/apps/sim/lib/workflows/executor/execution-core.test.ts index 048ba62492a..689ebe5b2d0 100644 --- a/apps/sim/lib/workflows/executor/execution-core.test.ts +++ b/apps/sim/lib/workflows/executor/execution-core.test.ts @@ -123,6 +123,7 @@ describe('executeWorkflowCore terminal finalization sequencing', () => { requestId: 'req-1', workflowId: 'workflow-1', userId: 'user-1', + workflowUserId: 'workflow-owner', workspaceId: 'workspace-1', triggerType: 'api', executionId: 'execution-1', @@ -755,4 +756,92 @@ describe('executeWorkflowCore terminal finalization sequencing', () => { expect(safeCompleteWithErrorMock).not.toHaveBeenCalled() expect(wasExecutionFinalizedByCore(envError, 'execution-no-log-start')).toBe(false) }) + + it('uses sessionUserId for env resolution when isClientSession is true', async () => { + const snapshot = { + ...createSnapshot(), + metadata: { + ...createSnapshot().metadata, + isClientSession: true, + sessionUserId: 'session-user', + workflowUserId: 'workflow-owner', + }, + } + + getPersonalAndWorkspaceEnvMock.mockResolvedValue({ + personalEncrypted: {}, + workspaceEncrypted: {}, + personalDecrypted: {}, + workspaceDecrypted: {}, + }) + safeStartMock.mockResolvedValue(true) + executorExecuteMock.mockResolvedValue({ + output: { done: true }, + logs: [], + metadata: { duration: 123, startTime: 'start', endTime: 'end' }, + }) + + await executeWorkflowCore({ + snapshot: snapshot as any, + callbacks: {}, + loggingSession: loggingSession as any, + }) + + expect(getPersonalAndWorkspaceEnvMock).toHaveBeenCalledWith('session-user', 'workspace-1') + }) + + it('uses workflowUserId for env resolution in server-side execution', async () => { + const snapshot = { + ...createSnapshot(), + metadata: { + ...createSnapshot().metadata, + isClientSession: false, + sessionUserId: undefined, + workflowUserId: 'workflow-owner', + userId: 'billing-actor', + }, + } + + getPersonalAndWorkspaceEnvMock.mockResolvedValue({ + personalEncrypted: {}, + workspaceEncrypted: {}, + personalDecrypted: {}, + workspaceDecrypted: {}, + }) + safeStartMock.mockResolvedValue(true) + executorExecuteMock.mockResolvedValue({ + output: { done: true }, + logs: [], + metadata: { duration: 123, startTime: 'start', endTime: 'end' }, + }) + + await executeWorkflowCore({ + snapshot: snapshot as any, + callbacks: {}, + loggingSession: loggingSession as any, + }) + + expect(getPersonalAndWorkspaceEnvMock).toHaveBeenCalledWith('workflow-owner', 'workspace-1') + }) + + it('throws when workflowUserId is missing in server-side execution', async () => { + const snapshot = { + ...createSnapshot(), + metadata: { + ...createSnapshot().metadata, + isClientSession: false, + sessionUserId: undefined, + workflowUserId: undefined, + userId: 'billing-actor', + }, + } + + await expect( + executeWorkflowCore({ + snapshot: snapshot as any, + callbacks: {}, + loggingSession: loggingSession as any, + }) + ).rejects.toThrow('Missing workflowUserId in execution metadata') + }) }) diff --git a/apps/sim/lib/workflows/executor/execution-core.ts b/apps/sim/lib/workflows/executor/execution-core.ts index aa96e5668a5..b5b352d701f 100644 --- a/apps/sim/lib/workflows/executor/execution-core.ts +++ b/apps/sim/lib/workflows/executor/execution-core.ts @@ -325,10 +325,13 @@ export async function executeWorkflowCore( const mergedStates = mergeSubblockStateWithValues(blocks) - const personalEnvUserId = metadata.sessionUserId || metadata.userId + const personalEnvUserId = + metadata.isClientSession && metadata.sessionUserId + ? metadata.sessionUserId + : metadata.workflowUserId if (!personalEnvUserId) { - throw new Error('Missing execution actor for environment resolution') + throw new Error('Missing workflowUserId in execution metadata') } const { personalEncrypted, workspaceEncrypted, personalDecrypted, workspaceDecrypted } =