{"record":{"id":"8e406b45ce18f796","repo":"n8n-io/n8n","slug":"execution-is-already-being-resumed-by-another-proc","errorCode":null,"errorMessage":"Execution is already being resumed by another process","messagePattern":"Execution is already being resumed by another process","errorType":"exception","errorClass":"ExecutionAlreadyResumingError","httpStatus":null,"severity":"warning","filePath":"packages/cli/src/active-executions.ts","lineNumber":138,"sourceCode":"\t\t\t\t}\n\n\t\t\t\tconst execution: Pick<IExecutionDb, 'id' | 'data' | 'waitTill' | 'status'> = {\n\t\t\t\t\tid: executionId,\n\t\t\t\t\tdata: executionData.executionData!,\n\t\t\t\t\twaitTill: null,\n\t\t\t\t\tstatus: executionStatus,\n\t\t\t\t};\n\n\t\t\t\tconst updateSucceeded = await this.executionPersistence.updateExistingExecution(\n\t\t\t\t\texecutionId,\n\t\t\t\t\texecution,\n\t\t\t\t\t// Only claim the execution if it is still in the status the caller expected\n\t\t\t\t\t{ requireStatus: existingExecution.expectedStatus },\n\t\t\t\t);\n\n\t\t\t\tif (!updateSucceeded) {\n\t\t\t\t\t// Another process is already resuming this execution\n\t\t\t\t\tthrow new ExecutionAlreadyResumingError(executionId);\n\t\t\t\t}\n\n\t\t\t\tif (existingExecution.expectedStatus === 'new') {\n\t\t\t\t\tawait this.executionRepository.setRunning(executionId);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tcapacityReservation.release();\n\t\t\tthrow error;\n\t\t}\n\n\t\tconst resumingExecution = this.activeExecutions[executionId];\n\t\tconst postExecutePromise = createDeferredPromise<IRun | undefined>();\n\n\t\tconst execution: IExecutingWorkflowData = {\n\t\t\texecutionData,\n\t\t\tstartedAt: resumingExecution?.startedAt ?? new Date(),\n\t\t\tpostExecutePromise,","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/cli/src/active-executions.ts#L120-L156","documentation":"In a multi-main (queue mode) deployment, resuming a waiting execution uses an optimistic-concurrency claim: updateExistingExecution only succeeds if the execution is still in the expected status. If the update reports failure, another main process already claimed and started resuming this execution, so the current attempt throws ExecutionAlreadyResumingError to avoid duplicate concurrent resumes. The capacity reservation is released before re-throwing.","triggerScenarios":"Two main processes receive a resume signal (webhook, wait-timer expiry, manual resume) for the same executionId near-simultaneously; the first wins the optimistic update, the second gets updateSucceeded === false and throws. Also possible after a leader election flap where multiple mains briefly believe they own the execution.","commonSituations":"Multi-main / queue-mode (Redis-backed) n8n deployments; webhook-triggered resumes hitting a load-balanced multi-main cluster; retry logic that re-issues a resume before the first completes; race during rolling restarts.","solutions":["Treat ExecutionAlreadyResumingError as benign: log it at info/debug and return success to the caller, since the execution is being handled by another main.","Ensure your resume trigger (webhook endpoint, timer worker) is idempotent and does not retry on this specific error.","Verify only one main is processing a given execution (check the active-executions ownership and queue topology)."],"exampleFix":"// before\nawait activeExecutions.resume(executionId, data);\n\n// after\ntry {\n  await activeExecutions.resume(executionId, data);\n} catch (e) {\n  if (e instanceof ExecutionAlreadyResumingError) {\n    logger.info('Execution already being resumed elsewhere', { executionId });\n    return;\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Before resuming, check the execution is still in a resumable status\nconst status = await executionRepository.findStatusForExecution(executionId);\nif (status !== 'waiting' && status !== 'new') {\n  // already being resumed or finished; skip\n  return;\n}","typeGuard":"import { ExecutionAlreadyResumingError } from 'n8n-workflow';\nfunction isAlreadyResuming(e: unknown): e is ExecutionAlreadyResumingError {\n  return e instanceof ExecutionAlreadyResumingError;\n}","tryCatchPattern":"try {\n  await activeExecutions.resume(executionId, data);\n} catch (e) {\n  if (e instanceof ExecutionAlreadyResumingError) {\n    logger.info('Execution already resuming elsewhere', { executionId });\n    return; // benign in multi-main\n  }\n  throw e;\n}","preventionTips":["In multi-main/queue mode, treat ExecutionAlreadyResumingError as benign and do not retry.","Make resume triggers (webhooks, timers) idempotent.","Monitor for excessive occurrences, which indicate duplicate resume signals or leader-election flapping."],"tags":["multi-main","execution","concurrency","queue-mode"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}