Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 35 additions & 26 deletions packages/cli/src/workflows/workflow.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
ListQueryDb,
WorkflowFolderUnionFull,
WorkflowHistoryUpdate,
WorkflowHistory,
} from '@n8n/db';
import {
SharedWorkflow,
Expand Down Expand Up @@ -48,6 +49,12 @@ import { WorkflowFinderService } from './workflow-finder.service';
import { WorkflowHistoryService } from './workflow-history/workflow-history.service';
import { WorkflowSharingService } from './workflow-sharing.service';

type RollbackPayload = {
active: boolean;
activeVersionId: string | null;
activeVersion: WorkflowHistory | null;
} & Partial<Omit<WorkflowEntity, 'active' | 'activeVersionId' | 'activeVersion'>>;

@Service()
export class WorkflowService {
constructor(
Expand Down Expand Up @@ -447,7 +454,14 @@ export class WorkflowService {
workflowId,
updatedWorkflow,
wasActive ? 'update' : 'activate',
workflow.versionId,
// If workflow could not be activated, set it again to inactive
// and revert the versionId and activeVersionId change so UI remains consistent
{
versionId: workflow.versionId,
active: false,
activeVersionId: null,
activeVersion: null,
},
);
}
}
Expand All @@ -457,45 +471,35 @@ export class WorkflowService {

/**
* Private helper to add a workflow to the active workflow manager
* @param originalVersionId - Optional versionId to roll back to if activation fails
* @param rollBackOptions - Optional rollback options
*/
private async _addToActiveWorkflowManager(
user: User,
workflowId: string,
workflow: WorkflowEntity,
mode: 'activate' | 'update',
originalVersionId?: string,
rollbackPayload: RollbackPayload,
): Promise<void> {
try {
await this.externalHooks.run('workflow.activate', [workflow]);
await this.activeWorkflowManager.add(workflowId, mode);
} catch (error) {
// If workflow could not be activated, set it again to inactive
// and revert the versionId and activeVersionId change so UI remains consistent
const rollbackPayload: QueryDeepPartialEntity<WorkflowEntity> = {
active: false,
activeVersion: null,
};

// Roll back versionId if provided (used in update flow)
if (originalVersionId !== undefined) {
rollbackPayload.versionId = originalVersionId;
}

await this.workflowRepository.update(workflowId, rollbackPayload);

// Also set it in the returned data
workflow.active = false;
workflow.activeVersionId = null;
workflow.activeVersion = null;
workflow.active = rollbackPayload.active;
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Activation failures now emit a workflow-deactivated event even when the workflow stays active, because the rollback payload can set active back to true. Condition the event emission on the rollback state so listeners aren’t told the workflow deactivated when it didn’t.

Prompt for AI agents
Address the following comment on packages/cli/src/workflows/workflow.service.ts at line 490:

<comment>Activation failures now emit a `workflow-deactivated` event even when the workflow stays active, because the rollback payload can set `active` back to true. Condition the event emission on the rollback state so listeners aren’t told the workflow deactivated when it didn’t.</comment>

<file context>
@@ -457,37 +471,25 @@ export class WorkflowService {
-			workflow.active = false;
-			workflow.activeVersionId = null;
-			workflow.activeVersion = null;
+			workflow.active = rollbackPayload.active;
+			workflow.activeVersionId = rollbackPayload.activeVersionId;
+			workflow.activeVersion = rollbackPayload.activeVersion;
</file context>

✅ Addressed in 836dfe8

workflow.activeVersionId = rollbackPayload.activeVersionId;
workflow.activeVersion = rollbackPayload.activeVersion;

// Emit deactivation event since activation failed
this.eventService.emit('workflow-deactivated', {
user,
workflowId,
workflow,
publicApi: false,
});
if (!workflow.activeVersionId) {
// Emit deactivation event since activation failed
this.eventService.emit('workflow-deactivated', {
user,
workflowId,
workflow,
publicApi: false,
});
}

let message;
if (error instanceof NodeApiError) message = error.description;
Expand Down Expand Up @@ -554,6 +558,7 @@ export class WorkflowService {

const updatedWorkflow = await this.workflowRepository.findOne({
where: { id: workflowId },
relations: ['activeVersion'],
});

if (!updatedWorkflow) {
Expand All @@ -567,7 +572,11 @@ export class WorkflowService {
publicApi: false,
});

await this._addToActiveWorkflowManager(user, workflowId, updatedWorkflow, 'activate');
await this._addToActiveWorkflowManager(user, workflowId, updatedWorkflow, 'activate', {
active: workflow.active,
activeVersionId: workflow.activeVersionId,
activeVersion: workflow.activeVersion,
});

return updatedWorkflow;
}
Expand Down
12 changes: 10 additions & 2 deletions packages/cli/src/workflows/workflows.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,11 @@ export class WorkflowsController {
workflowId,
req.user,
['workflow:read'],
{ includeTags: !this.globalConfig.tags.disabled, includeParentFolder: true },
{
includeTags: !this.globalConfig.tags.disabled,
includeParentFolder: true,
includeActiveVersion: true,
},
);

if (!workflow) {
Expand Down Expand Up @@ -371,7 +375,11 @@ export class WorkflowsController {
workflowId,
req.user,
['workflow:read'],
{ includeTags: !this.globalConfig.tags.disabled, includeParentFolder: true },
{
includeTags: !this.globalConfig.tags.disabled,
includeParentFolder: true,
includeActiveVersion: true,
},
);

if (!workflow) {
Expand Down
Loading
Loading