elsa-workflows/elsa-core · error · InvalidOperationException
Outbox item does not contain a dispatch command for kind .
Error message
Outbox item {item.Id} does not contain a dispatch command for kind {item.Kind}. What it means
WorkflowDispatchOutboxProcessor.SendAsync switches on the outbox item's Kind and sends the corresponding dispatch command. When the item's kind-specific command payload (TriggerWorkflowsCommand or ResumeWorkflowsCommand) is null, the default case throws InvalidOperationException stating the item does not contain a dispatch command for its kind. This is a data-integrity guard against corrupt/partially written outbox items.
Solutions
- Inspect the offending outbox item record and delete or repair it (it cannot be dispatched without its command payload).
- Verify outbox item serialization/deserialization matches across writer and processor versions (schema alignment after upgrades).
- Fix the producer code path so it always assigns the command matching the Kind before saving the item.
- Catch the InvalidOperationException in ProcessAsync and route the item to a dead-letter/failure state instead of stalling the processor loop.
Example fix
// before
var item = new WorkflowDispatchOutboxItem
{
Kind = WorkflowDispatchOutboxItemKind.TriggerWorkflows
// TriggerWorkflowsCommand never set
};
// after
var item = new WorkflowDispatchOutboxItem
{
Kind = WorkflowDispatchOutboxItemKind.TriggerWorkflows,
TriggerWorkflowsCommand = triggerCommand
};
if (item.TriggerWorkflowsCommand == null && item.ResumeWorkflowsCommand == null)
throw new InvalidOperationException("Outbox item created without a dispatch command."); Defensive patterns
Strategy: try-catch
Validate before calling
if (item.TriggerWorkflowsCommand == null && item.ResumeWorkflowsCommand == null)
logger.LogError("Outbox item {Id} has no dispatch command payload; routing to dead-letter.", item.Id); Type guard
bool HasDispatchCommand(WorkflowDispatchOutboxItem item) =>
(item.Kind == WorkflowDispatchOutboxItemKind.TriggerWorkflows && item.TriggerWorkflowsCommand != null) ||
(item.Kind == WorkflowDispatchOutboxItemKind.ResumeWorkflows && item.ResumeWorkflowsCommand != null); Try / catch
try
{
await SendAsync(item, headers, ct);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("does not contain a dispatch command"))
{
await MarkItemFailedAsync(item, ex, ct);
} Prevention
- Always set the command payload matching the item Kind before saving
- Keep outbox schema/serialization aligned across upgrades
- Route payload-less items to a failure state instead of crashing the loop
When it happens
Trigger: Processing an outbox item whose Kind says TriggerWorkflows or ResumeWorkflows but whose corresponding command property is null — typically due to a serialization/write failure when the item was saved, or a bug constructing the item.
Common situations: Upgrade/migration changing the outbox item schema so payloads fail to deserialize; partial writes to the outbox store; manually inserted or test-fabricated items missing payloads; message-envelope deserialization dropping unknown fields.
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
- Drain already in progress; second invocation rejected
- Drain already completed in this generation; subsequent…
- Cannot overwrite an AI conversation that belongs to another…
- Cannot overwrite an AI proposal that belongs to another…
- Cannot overwrite an AI proposal that belongs to another…
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/03f92554a1a31e63.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Workflows.Runtime/Services/WorkflowDispatchOutboxProcessor.cs:168
{
var headers = TenantHeaders.CreateHeaders(item.TenantId);
switch (item.Kind)
{
case WorkflowDispatchOutboxItemKind.WorkflowDefinition when item.WorkflowDefinitionCommand != null:
await commandSender.SendAsync(item.WorkflowDefinitionCommand, CommandStrategy.Background, headers, cancellationToken);
break;
case WorkflowDispatchOutboxItemKind.WorkflowInstance when item.WorkflowInstanceCommand != null:
await commandSender.SendAsync(item.WorkflowInstanceCommand, CommandStrategy.Background, headers, cancellationToken);
break;
case WorkflowDispatchOutboxItemKind.TriggerWorkflows when item.TriggerWorkflowsCommand != null:
await commandSender.SendAsync(item.TriggerWorkflowsCommand, CommandStrategy.Background, headers, cancellationToken);
break;
case WorkflowDispatchOutboxItemKind.ResumeWorkflows when item.ResumeWorkflowsCommand != null:
await commandSender.SendAsync(item.ResumeWorkflowsCommand, CommandStrategy.Background, headers, cancellationToken);
break;
default:
throw new InvalidOperationException($"Outbox item {item.Id} does not contain a dispatch command for kind {item.Kind}.");
}
logger.LogDebug("Delivered workflow dispatch outbox item {OutboxItemId} for owner workflow {WorkflowInstanceId}", item.Id, item.OwnerWorkflowInstanceId);
}
private async Task HandleMissingOwnerAsync(WorkflowDispatchOutboxItem item, CancellationToken cancellationToken)
{
var retention = dispatcherOptions.Value.OrphanedOutboxItemRetention;
var expiresAt = item.CreatedAt.Add(retention);
if (retention <= TimeSpan.Zero || systemClock.UtcNow >= expiresAt)
{
logger.LogWarning("Deleting workflow dispatch outbox item {OutboxItemId} because owner workflow {WorkflowInstanceId} was not found and the orphan retention period has elapsed", item.Id, item.OwnerWorkflowInstanceId);
await TryDeleteRetainedItemAsync(item, cancellationToken);
return;
}
logger.LogDebug("Skipping workflow dispatch outbox item {OutboxItemId} because owner workflow {WorkflowInstanceId} was not found; it will be retained until {ExpiresAt}", item.Id, item.OwnerWorkflowInstanceId, expiresAt);View on GitHub (pinned to fe9217bdfa)