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

  1. Ensure the DeleteContentTask targets a different content type than the ContentDeletedEvent trigger.
  2. 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.
  3. Restructure cascade deletes using content handlers/relations instead of an inline self-referencing workflow.
  4. 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

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


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)