OrchardCMS/OrchardCore · error · InvalidOperationException
The 'DeleteContentTask' can't delete the content item as it…
Error message
The 'DeleteContentTask' can't delete the content item as it is executed inline from a starting 'ContentDeletedEvent' of the same content type, which would result in an infinitive loop.
What it means
DeleteContentTask throws this InvalidOperationException when it runs inline from a starting ContentDeletedEvent for a content item of the same type. Deleting the item inline would emit another ContentDeletedEvent, restarting the workflow and looping forever. Orchard Core guards against this recursion explicitly.
Solutions
- Ensure the DeleteContentTask targets a different content type than the ContentDeletedEvent trigger.
- Note the task already Noops when the InlineEvent.ContentItemId equals the target's ContentItemId — check your expression does not resolve to the same item of the same type.
- Restructure cascade deletes using content handlers/relations instead of an inline self-referencing workflow.
- Catch InvalidOperationException in host workflow execution code and log the suppressed recursion.
Example fix
// before
// Trigger: ContentDeletedEvent { ContentType: "Topic" }, DeleteContentTask resolves a 'Topic' item
// after
DeleteContentTask { ContentItemId: '{{ related.ReplyContentItemId }}' } // delete a different type Defensive patterns
Strategy: validation
Validate before calling
// Before triggering a ContentDeletedEvent-started workflow
if (triggerIsStart && triggerEvent == "ContentDeletedEvent" &&
string.Equals(resolvedTargetContentType, triggerContentType, StringComparison.OrdinalIgnoreCase) &&
string.Equals(resolvedTargetId, inlineEventContentItemId, StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException("DeleteContentTask would recurse on the same item/type; reconfigure."); Try / catch
try { await workflowManager.TriggerEventAsync(nameof(ContentDeletedEvent), contentItem); }
catch (InvalidOperationException ex) when (ex.Message.Contains("DeleteContentTask") && ex.Message.Contains("infinitive loop"))
{
logger.LogWarning(ex, "Delete loop prevented; reconfigure cascade workflow.");
} Prevention
- Cascade-delete related content via content handlers, not self-referencing inline workflows.
- Keep the delete target's content type different from the ContentDeletedEvent trigger type.
- Remember the built-in Noop only protects the exact same ContentItemId, not same-type siblings.
- Test deletion workflows with multi-item cascades.
When it happens
Trigger: Workflow starting with ContentDeletedEvent (IsStart=true, ContentType = X) containing a DeleteContentTask whose resolved contentItem.ContentType equals X and whose target is triggered inline during deletion of an X item.
Common situations: Cleanup workflows like 'when a Session is deleted, delete related materials' where the related content type is accidentally set to the same type; recursive cascade-delete chains between two types that reference each other.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- The 'CreateContentTask' can't update the content item as it…
- The 'CreateContentTask' can't create the content item as it…
- The 'CreateContentTask' can't publish the content item as…
- The 'CreateContentTask' can't create the content item as it…
- The 'PublishContentTask' can't publish the content item as…
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/c21bc50bd900be06.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/DeleteContentTask.cs:52
{
return Outcome("Noop");
}
var contentItem = await ContentManager.GetAsync(content.ContentItem.ContentItemId, VersionOptions.Latest);
if (contentItem == null)
{
if (content is ContentItemIdExpressionResult)
{
throw new InvalidOperationException($"The '{nameof(DeleteContentTask)}' failed to retrieve the content item.");
}
contentItem = content.ContentItem;
}
if (InlineEvent.IsStart && InlineEvent.ContentType == contentItem.ContentType && InlineEvent.Name == nameof(ContentDeletedEvent))
{
throw new InvalidOperationException($"The '{nameof(DeleteContentTask)}' can't delete the content item as it is executed inline from a starting '{nameof(ContentDeletedEvent)}' of the same content type, which would result in an infinitive loop.");
}
await ContentManager.RemoveAsync(contentItem);
return Outcome("Deleted");
}
}
View on GitHub (pinned to 4306c0717f)