elsa-workflows/elsa-core · error · FaultException
AlterationFaultCodes.PlanNotFound
AlterationFaultCodes.PlanNotFound
Error message
Alteration Plan with ID {planId} not found. What it means
The DispatchAlterationJobs activity finds the alteration plan via IAlterationPlanStore.FindAsync using a filter on the PlanId input and throws a FaultException with code AlterationFaultCodes.PlanNotFound when the lookup returns null. Like CompleteAlterationPlan, this is a workflow fault so callers can catch it and route the workflow into an error path.
Solutions
- Verify the PlanId input refers to a persisted alteration plan visible to the current tenant.
- Persist the plan before dispatching jobs for it.
- Add a workflow fault handler for AlterationFaultCodes.PlanNotFound to handle missing plans.
- Confirm the store connection/tenant scope used at runtime matches where the plan was saved.
Example fix
// before
await workflowInvoker.DispatchAlterationJobsAsync(planId); // planId may not exist
// after
var plan = await alterationPlanStore.FindAsync(new AlterationPlanFilter { Id = planId }, ct);
if (plan == null)
throw new InvalidOperationException($"Cannot dispatch jobs: plan {planId} not found.");
await workflowInvoker.DispatchAlterationJobsAsync(planId); Defensive patterns
Strategy: try-catch
Validate before calling
var plan = await alterationPlanStore.FindAsync(new AlterationPlanFilter { Id = planId }, ct);
if (plan is null)
throw new InvalidOperationException($"Plan '{planId}' not found; ensure it is persisted and tenant-visible before dispatching jobs."); Type guard
bool PlanExists(AlterationPlan? plan) => plan is not null;
Try / catch
try
{
await workflow.RunAsync(instance, ct);
}
catch (FaultException ex) when (ex.Code == AlterationFaultCodes.PlanNotFound)
{
logger.LogError("Cannot dispatch alteration jobs: {Message}", ex.Message);
} Prevention
- Persist the alteration plan before scheduling DispatchAlterationJobs.
- Pass plan IDs from trusted store lookups, not raw external input.
- Handle AlterationFaultCodes.PlanNotFound in workflow fault handlers to fail gracefully.
When it happens
Trigger: Executing DispatchAlterationJobs with a PlanId input for which FindAsync returns no plan (nonexistent ID, deleted plan, wrong tenant scope, or plan not yet persisted).
Common situations: Dispatching jobs for a plan created in a different tenant; race where the plan was deleted after being queued; misspelled or stale plan ID stored in workflow input from an earlier failed run.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- AlterationFaultCodes.PlanNotFound
- Workflow definition with ID
- An alteration job with ID
- An alteration plan with ID
- AlterationFaultCodes.PlanNotFound
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/3c3af258e2b491be.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Alterations/Activities/DispatchAlterationJobs.cs:50
/// <summary>
/// The ID of the alteration plan.
/// </summary>
public Input<string> PlanId { get; set; } = null!;
/// <inheritdoc />
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var cancellationToken = context.CancellationToken;
var planId = context.Get(PlanId)!;
var alterationPlanStore = context.GetRequiredService<IAlterationPlanStore>();
var planFilter = new AlterationPlanFilter
{
Id = planId
};
var plan = await alterationPlanStore.FindAsync(planFilter, cancellationToken);
if (plan == null)
throw new FaultException(AlterationFaultCodes.PlanNotFound, AlterationFaultCategories.Alteration, DefaultFaultTypes.System, $"Alteration Plan with ID {planId} not found.");
// Update status.
plan.Status = AlterationPlanStatus.Dispatching;
await alterationPlanStore.SaveAsync(plan, cancellationToken);
// Find all jobs for the plan and dispatch them.
var filter = new AlterationJobFilter
{
PlanId = plan.Id
};
var alterationJobStore = context.GetRequiredService<IAlterationJobStore>();
var alterationJobIds = await alterationJobStore.FindManyIdsAsync(filter, cancellationToken);
// Dispatch each job.
var alterationJobDispatcher = context.GetRequiredService<IAlterationJobDispatcher>();
foreach (var jobId in alterationJobIds)
await alterationJobDispatcher.DispatchAsync(jobId, cancellationToken);
View on GitHub (pinned to fe9217bdfa)