elsa-workflows/elsa-core · error · InvalidOperationException
No alteration log found in the transient properties.
Error message
No alteration log found in the transient properties.
What it means
Same middleware as the alterations error, but this one fires when the AlterationsLogPropertyKey entry is missing from TransientProperties. The middleware needs an AlterationLog to record what each alteration did, so it throws InvalidOperationException when the log was not seeded alongside the alterations.
Solutions
- Seed both properties together: context.TransientProperties.SetValue(RunAlterationsMiddleware.AlterationsLogPropertyKey, new AlterationLog()) next to the alterations value.
- Extract a helper that stages alterations and log in one call to keep them in sync.
- Verify against the intended alterations-run API instead of hand-seeding properties.
- Check resumed/replayed executions also re-seed the log.
Example fix
// before context.TransientProperties.SetValue(RunAlterationsMiddleware.AlterationsPropertyKey, alterations); // after context.TransientProperties.SetValue(RunAlterationsMiddleware.AlterationsPropertyKey, alterations); context.TransientProperties.SetValue(RunAlterationsMiddleware.AlterationsLogPropertyKey, new AlterationLog());
Defensive patterns
Strategy: type-guard
Validate before calling
var hasLog = context.TransientProperties.GetValue(RunAlterationsMiddleware.AlterationsLogPropertyKey) is AlterationLog;
if (!hasLog) throw new InvalidOperationException("Seed an AlterationLog before running the workflow."); Type guard
bool TryGetAlterationLog(WorkflowExecutionContext ctx, out AlterationLog log)
{
log = ctx.TransientProperties.GetValue(RunAlterationsMiddleware.AlterationsLogPropertyKey) as AlterationLog;
return log != null;
} Try / catch
try
{
await workflowRunner.RunAsync(workflow, workflowState);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("No alteration log found in the transient properties"))
{
logger.LogError(ex, "Alteration log was not seeded alongside alterations.");
} Prevention
- Always set AlterationsPropertyKey and AlterationsLogPropertyKey together in one helper.
- Construct the AlterationLog up front, even if empty, rather than lazily.
- Cover the alteration-run path with a test that asserts both properties are seeded.
When it happens
Trigger: Running a workflow with RunAlterationsMiddleware while TransientProperties contains AlterationsPropertyKey but not AlterationsLogPropertyKey — i.e., the caller seeded the alterations but forgot the log (or seeded them at different pipeline stages).
Common situations: Manually seeding only the alterations key; a custom alteration runner that sets the list but constructs the log lazily after the middleware already checked; partial refactoring where the log seeding line was dropped.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- No alterations found in the transient properties.
- AlterationFaultCodes.PlanNotFound
- Multiple Invoke methods were found. Use either Invoke or…
- No Invoke methods were found. Use either Invoke or…
- The method must return Task or ValueTask
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/e6465e4bd17a209e.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Alterations/Middleware/Workflows/RunAlterationsMiddleware.cs:21
using Elsa.Alterations.Core.Models;
using Elsa.Extensions;
using Elsa.Workflows;
using Elsa.Workflows.Pipelines.WorkflowExecution;
namespace Elsa.Alterations.Middleware.Workflows;
/// <summary>
/// Middleware that runs alterations.
/// </summary>
internal class RunAlterationsMiddleware(WorkflowMiddlewareDelegate next, IEnumerable<IAlterationHandler> handlers) : WorkflowExecutionMiddleware(next)
{
public static readonly object AlterationsPropertyKey = new();
public static readonly object AlterationsLogPropertyKey = new();
public override async ValueTask InvokeAsync(WorkflowExecutionContext context)
{
var alterations = (IEnumerable<IAlteration>)(context.TransientProperties.GetValue(AlterationsPropertyKey) ?? throw new InvalidOperationException("No alterations found in the transient properties."));
var log = (AlterationLog)(context.TransientProperties.GetValue(AlterationsLogPropertyKey) ?? throw new InvalidOperationException("No alteration log found in the transient properties."));
await RunAsync(context, alterations, log, context.CancellationToken);
}
private async Task RunAsync(WorkflowExecutionContext workflowExecutionContext, IEnumerable<IAlteration> alterations, AlterationLog log, CancellationToken cancellationToken = default)
{
var commitActions = new List<Func<Task>>();
foreach (var alteration in alterations)
{
// Find handlers.
var supportedHandlers = handlers.Where(x => x.CanHandle(alteration)).ToList();
foreach (var handler in supportedHandlers)
{
// Execute handler.
var alterationContext = new AlterationContext(alteration, workflowExecutionContext, log, cancellationToken);
await handler.HandleAsync(alterationContext);
View on GitHub (pinned to fe9217bdfa)