elsa-workflows/elsa-core · error · FaultException

AlterationFaultCodes.PlanNotFound

AlterationFaultCodes.PlanNotFound

Error message

Alteration Plan with ID {planId} not found.

What it means

The GenerateAlterations activity's GetPlanAsync looked up an AlterationPlan by ID via IAlterationPlanStore.FindAsync and found nothing. It then throws a FaultException with AlterationFaultCodes.PlanNotFound, which surfaces as a workflow fault rather than a plain exception. This guards the alteration pipeline against running with a missing or already-deleted plan.

Solutions

  1. Verify the planId exists before scheduling the workflow by querying the alteration plan store (or API) for that ID.
  2. Check that the workflow is running against the same persistence store/environment where the plan was created.
  3. Confirm the plan was not removed by cleanup or a prior workflow run; re-create it if needed.
  4. Catch FaultException with code AlterationFaultCodes.PlanNotFound in the workflow's fault handler and surface a user-friendly message.

Example fix

// before
await workflowInput.Set("PlanId", copiedGuidString);

// after
var plan = await alterationPlanStore.FindAsync(new AlterationPlanFilter { Id = planId });
if (plan == null)
    throw new InvalidOperationException($"Plan {planId} does not exist; create it before running GenerateAlterations.");
Defensive patterns

Strategy: validation

Validate before calling

var plan = await alterationPlanStore.FindAsync(new AlterationPlanFilter { Id = planId });
if (plan == null)
    throw new InvalidOperationException($"Plan {planId} not found; aborting before scheduling alteration workflow.");

Try / catch

try
{
    await generateAlterationsActivity.ExecuteAsync(context);
}
catch (FaultException ex) when (ex.FaultCode == AlterationFaultCodes.PlanNotFound)
{
    logger.LogWarning("Alteration plan {PlanId} not found", planId);
}

Prevention

When it happens

Trigger: Calling GenerateAlterations (or otherwise executing GetPlanAsync) with a planId for which IAlterationPlanStore.FindAsync(planFilter) returns null — i.e., the ID does not exist in the plan store.

Common situations: Passing a hardcoded or copied plan ID that was never created; the plan was deleted by retention/cleanup before the workflow ran; using an ID from a different database/environment (staging vs production); a typo or GUID casing/whitespace issue in workflow input.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/4e211f54070ef88f. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.Alterations/Activities/GenerateAlterationJobs.cs:66

        if (workflowInstanceIds.Any())
            await GenerateJobsAsync(context, plan, workflowInstanceIds);

        context.SetResult(workflowInstanceIds.Count);
    }

    private async Task<AlterationPlan> GetPlanAsync(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.");

        return plan;
    }

    private async Task UpdatePlanStatusAsync(ActivityExecutionContext context, AlterationPlan plan)
    {
        var cancellationToken = context.CancellationToken;
        var alterationPlanStore = context.GetRequiredService<IAlterationPlanStore>();
        plan.Status = AlterationPlanStatus.Generating;
        await alterationPlanStore.SaveAsync(plan, cancellationToken);
    }

    private async Task<IEnumerable<string>> FindMatchingWorkflowInstanceIdsAsync(ActivityExecutionContext context, AlterationWorkflowInstanceFilter filter)
    {
        var cancellationToken = context.CancellationToken;
        var workflowInstanceFinder = context.GetRequiredService<IWorkflowInstanceFinder>();
        return await workflowInstanceFinder.FindAsync(filter, cancellationToken);
    }

View on GitHub (pinned to fe9217bdfa)