mastra-ai/mastra · error · MastraError
WORKFLOW_RESUME_ALREADY_CLAIMED
WORKFLOW_RESUME_ALREADY_CLAIMED
Error message
This suspended workflow run was already resumed by another caller. Workflow "${this.workflowId}" run "${this.runId}" moved from "${snapshot.status}" to "${current.status}" before this resume could claim it. Only one resume() call may continue a given suspension; re-read the run state before resuming again. What it means
A MastraError (id WORKFLOW_RESUME_ALREADY_CLAIMED, USER category) thrown when two callers try to resume the same suspended run concurrently. The store's compare-and-set (status 'suspended' -> 'running') failed, meaning another resume already claimed the run; the error reports the status transition it observed. Only one resume() may continue a given suspension.
Source
Thrown at packages/core/src/workflows/workflow.ts:4369
opts: { status: 'running', expectedStatus: 'suspended' },
});
if (claimed) {
return;
}
// The compare-and-set found a status other than `suspended`. Re-read so the error names the
// status the run actually landed in rather than guessing.
const current = await workflowsStore.loadWorkflowSnapshot({
workflowName: this.workflowId,
runId: this.runId,
});
if (!current) {
throw new Error('No snapshot found for this workflow run: ' + this.workflowId + ' ' + this.runId);
}
throw new MastraError({
id: 'WORKFLOW_RESUME_ALREADY_CLAIMED',
domain: ErrorDomain.MASTRA_WORKFLOW,
category: ErrorCategory.USER,
text:
`This suspended workflow run was already resumed by another caller. Workflow "${this.workflowId}" run "${this.runId}" ` +
`moved from "${snapshot.status}" to "${current.status}" before this resume could claim it. ` +
`Only one resume() call may continue a given suspension; re-read the run state before resuming again.`,
details: {
workflowId: this.workflowId,
runId: this.runId,
expectedStatus: 'suspended',
actualStatus: current.status ?? 'unknown',
},
});
}
protected async _resume<TResume>(
params: {View on GitHub (pinned to 75dd419e61)
Solutions
- Treat as expected contention: catch it, re-load the run state, and skip if status is 'running'/'completed'.
- Make resume idempotent on your side — key the trigger (webhook/job) by runId so duplicates are dropped.
- Check the run's current status via getWorkflowRunState() before issuing another resume.
- If using a store without concurrent-update support, note resumes cannot be de-duplicated atomically and guard at the application level (lock/queue).
Example fix
// before
await run.resume({ resumeData });
// after
try {
await run.resume({ resumeData });
} catch (e) {
if ((e as any).id === 'WORKFLOW_RESUME_ALREADY_CLAIMED') {
const state = await run.getWorkflowRunState(); // another caller resumed; re-read and no-op
return;
}
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
const state = await run.getWorkflowRunState(); if (state.status !== 'suspended') return; // someone else already advanced it
Type guard
function isAlreadyClaimed(e: unknown): e is { id: 'WORKFLOW_RESUME_ALREADY_CLAIMED' } {
return typeof e === 'object' && e !== null && (e as any).id === 'WORKFLOW_RESUME_ALREADY_CLAIMED';
} Try / catch
try {
await run.resume({ resumeData, step });
} catch (e) {
if (isAlreadyClaimed(e)) {
logger.info('Resume already claimed by another caller; skipping');
return;
}
throw e;
} Prevention
- Make resume triggers idempotent (dedupe by runId).
- Use a storage adapter with concurrent-update support so CAS claiming works.
- Re-read run status before issuing another resume.
- Avoid polling resume() on a timer; resume once per suspension event.
When it happens
Trigger: Two resume() calls (e.g. from two server instances, a webhook retry plus a poller, or double-clicked UI) racing on the same runId while the storage adapter supports concurrent updates (updateWorkflowState CAS).
Common situations: At-least-once webhook deliveries retried resume calls; horizontally scaled deployments sharing one database; frontend firing resume twice; job scheduler retries after a timeout that actually succeeded.
Related errors
- error.message (workflow resume already claimed conflict)
- ${file} was opened by another process during compaction — is
- Cannot resume workflow: workflows store is required
- Cannot resume workflow: no snapshot found for runId ${this.r
- This workflow run was not suspended
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/9aea56e65dcb1452.
Report an issue: GitHub.