{"record":{"id":"03f92554a1a31e63","repo":"elsa-workflows/elsa-core","slug":"outbox-item-item-id-does-not-contain-a-dispatch-command-for","errorCode":null,"errorMessage":"Outbox item {item.Id} does not contain a dispatch command for kind {item.Kind}.","messagePattern":"Outbox item (.+?) does not contain a dispatch command for kind (.+?)\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.Workflows.Runtime/Services/WorkflowDispatchOutboxProcessor.cs","lineNumber":168,"sourceCode":"    {\n        var headers = TenantHeaders.CreateHeaders(item.TenantId);\n\n        switch (item.Kind)\n        {\n            case WorkflowDispatchOutboxItemKind.WorkflowDefinition when item.WorkflowDefinitionCommand != null:\n                await commandSender.SendAsync(item.WorkflowDefinitionCommand, CommandStrategy.Background, headers, cancellationToken);\n                break;\n            case WorkflowDispatchOutboxItemKind.WorkflowInstance when item.WorkflowInstanceCommand != null:\n                await commandSender.SendAsync(item.WorkflowInstanceCommand, CommandStrategy.Background, headers, cancellationToken);\n                break;\n            case WorkflowDispatchOutboxItemKind.TriggerWorkflows when item.TriggerWorkflowsCommand != null:\n                await commandSender.SendAsync(item.TriggerWorkflowsCommand, CommandStrategy.Background, headers, cancellationToken);\n                break;\n            case WorkflowDispatchOutboxItemKind.ResumeWorkflows when item.ResumeWorkflowsCommand != null:\n                await commandSender.SendAsync(item.ResumeWorkflowsCommand, CommandStrategy.Background, headers, cancellationToken);\n                break;\n            default:\n                throw new InvalidOperationException($\"Outbox item {item.Id} does not contain a dispatch command for kind {item.Kind}.\");\n        }\n\n        logger.LogDebug(\"Delivered workflow dispatch outbox item {OutboxItemId} for owner workflow {WorkflowInstanceId}\", item.Id, item.OwnerWorkflowInstanceId);\n    }\n\n    private async Task HandleMissingOwnerAsync(WorkflowDispatchOutboxItem item, CancellationToken cancellationToken)\n    {\n        var retention = dispatcherOptions.Value.OrphanedOutboxItemRetention;\n        var expiresAt = item.CreatedAt.Add(retention);\n\n        if (retention <= TimeSpan.Zero || systemClock.UtcNow >= expiresAt)\n        {\n            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);\n            await TryDeleteRetainedItemAsync(item, cancellationToken);\n            return;\n        }\n\n        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);","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Workflows.Runtime/Services/WorkflowDispatchOutboxProcessor.cs#L150-L186","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nvar item = new WorkflowDispatchOutboxItem\n{\n    Kind = WorkflowDispatchOutboxItemKind.TriggerWorkflows\n    // TriggerWorkflowsCommand never set\n};\n\n// after\nvar item = new WorkflowDispatchOutboxItem\n{\n    Kind = WorkflowDispatchOutboxItemKind.TriggerWorkflows,\n    TriggerWorkflowsCommand = triggerCommand\n};\nif (item.TriggerWorkflowsCommand == null && item.ResumeWorkflowsCommand == null)\n    throw new InvalidOperationException(\"Outbox item created without a dispatch command.\");","handlingStrategy":"try-catch","validationCode":"if (item.TriggerWorkflowsCommand == null && item.ResumeWorkflowsCommand == null)\n    logger.LogError(\"Outbox item {Id} has no dispatch command payload; routing to dead-letter.\", item.Id);","typeGuard":"bool HasDispatchCommand(WorkflowDispatchOutboxItem item) =>\n    (item.Kind == WorkflowDispatchOutboxItemKind.TriggerWorkflows && item.TriggerWorkflowsCommand != null) ||\n    (item.Kind == WorkflowDispatchOutboxItemKind.ResumeWorkflows && item.ResumeWorkflowsCommand != null);","tryCatchPattern":"try\n{\n    await SendAsync(item, headers, ct);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"does not contain a dispatch command\"))\n{\n    await MarkItemFailedAsync(item, ex, ct);\n}","preventionTips":["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"],"tags":["outbox","runtime","data-integrity","invalid-operation"],"backgroundTag":"internal-invariant-violation","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}