OrchardCMS/OrchardCore · error · InvalidOperationException

The cloned content item doesn't contain an AutoroutePart.

Error message

The cloned content item doesn't contain an AutoroutePart.

What it means

AutoroutePartHandler.CloningAsync generates a unique absolute path for the cloned item, but first requires the clone (CloneContentItem) to already carry an AutoroutePart. If the cloned content item lacks that part it throws InvalidOperationException, indicating the clone operation produced an item inconsistent with the source.

Solutions

  1. Ensure the content type definition includes AutoroutePart on both source and clone before cloning.
  2. Use the standard IContentManager clone APIs instead of manually constructing the clone item.
  3. Attach an AutoroutePart to the clone before the Cloning handler runs, copying Path from the source.
  4. Run a migration to re-add AutoroutePart to legacy items missing it.

Example fix

// before
var clone = new ContentItem();
await _contentManager.CloneAsync(contentItem);
// after
var clone = contentItem.Clone();
clone.Weld(new AutoroutePart());
await _contentManager.CreateAsync(clone);
Defensive patterns

Strategy: validation

Validate before calling

if (!clone.TryGet<AutoroutePart>(out _)) throw new InvalidOperationException("Clone must carry AutoroutePart before cloning.");

Type guard

static bool HasAutoroute(ContentItem item) => item.TryGet<AutoroutePart>(out _);

Try / catch

try { await _contentManager.CloneAsync(item); }
catch (InvalidOperationException ex) when (ex.Message.Contains("AutoroutePart"))
{
    logger.LogError(ex, "Clone of {Id} lacks AutoroutePart", item.ContentItemId);
    throw new ValidationException("Source type must include AutoroutePart for cloning.");
}

Prevention

When it happens

Trigger: Cloning a content item whose type has AutoroutePart attached to the source but where the clone pipeline removed/did not copy the part (e.g. clone created manually without parts, or type definitions changed between source and clone).

Common situations: Custom code calling IContentManager.CloneAsync with a hand-built clone content item; a content type where AutoroutePart was removed after items were created; recipe/import flows constructing clones without copying all parts.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13). Data as JSON: /api/errors/f55c6a7595dcfed1. Report an issue: GitHub.

Appendix: source

Thrown at src/OrchardCore.Modules/OrchardCore.Autoroute/Handlers/AutoroutePartHandler.cs:151

    }

    public override async Task CreatedAsync(CreateContentContext context, AutoroutePart part)
    {
        await GenerateContainerPathFromPatternAsync(part);
        await GenerateContainedPathsFromPatternAsync(context.ContentItem, part);
    }

    public override async Task UpdatedAsync(UpdateContentContext context, AutoroutePart part)
    {
        await GenerateContainerPathFromPatternAsync(part);
        await GenerateContainedPathsFromPatternAsync(context.ContentItem, part);
    }

    public override async Task CloningAsync(CloneContentContext context, AutoroutePart part)
    {
        if (!context.CloneContentItem.TryGet<AutoroutePart>(out var clonedPart))
        {
            throw new InvalidOperationException("The cloned content item doesn't contain an AutoroutePart.");
        }

        clonedPart.Path = await GenerateUniqueAbsolutePathAsync(part.Path, context.CloneContentItem.ContentItemId);
        clonedPart.SetHomepage = false;
        clonedPart.Apply();

        await GenerateContainedPathsFromPatternAsync(context.CloneContentItem, part);
    }

    public override Task GetContentItemAspectAsync(ContentItemAspectContext context, AutoroutePart part)
    {
        return context.ForAsync<RouteHandlerAspect>(async aspect =>
        {
            var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType);
            var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "AutoroutePart", StringComparison.Ordinal));
            var settings = contentTypePartDefinition.GetSettings<AutoroutePartSettings>();
            if (settings.ManageContainedItemRoutes)
            {

View on GitHub (pinned to 4306c0717f)