Confusing duplicate content type error

Confusing duplicate content type error header image

Working in a collaboration scenario for Office 365 you usually work with content types. Now when working with Office 365 you can use the Add-in Model to provision these as described in Programmatically creating Site Columns and Content Types using the App Model by Waldek. Or you can use the PnP Create Content Types sample to deploy them.

The basics are pretty simple, all you need to do is to fill the ContentTypeCreationInformation class:

$parentCt = new-object Microsoft.SharePoint.Client.ContentTypeCreationInformation;
$parentCt.Name="Parent ContentType"
$parentCt.Description="Sample"
$parentCt.Id = "0x010100C0B9283FC7311C488917E5A9876B01FC"
$parentCt.Group= "Sample"
$ct = $ctCollection.Add($parentCt)

When you provision the ID rather than using the ParentID property you have some control and can deploy the content type on different locations with the same ID. Something that allows you to do more complex search queries and will help you to identify content based on the content type.

A duplicate content type was found when creating content types through the UI

In a recent scenario we encountered some strange issues when creating a new content type through the UI. When creating that content type and deriving it from a content type that was provisioned using PnP got an error stating A Duplicate content type “Name” was found. Even tough there was not a single content type with that name present.

As it turns out it had to do with the content type id’s. When checking the article Content Type Id’s on Technet you see some examples on how the content type id’s are determined. When you create content types they will add +01 at the end for child content types, and there is a SQL table that gets an update to determine what the last ID is. When you delete a child content type and add a new one it will check the database for the last id and do a +01. If you export your site you will get all the available id’s.

In an on premises set-up you can find the dbo.ContentTypes table in the content database there you can find NextChildByte. That is what lets SharePoint know what that next numerical value should be.

So imagine that create the following content types:

  • Parent with id 0x010100C0B9283FC7311C488917E5A9876B01FC
  • Child 1 with id 0x010100C0B9283FC7311C488917E5A9876B01FC01
  • Child 2 with id 0x010100C0B9283FC7311C488917E5A9876B01FC02
  • Child 3 with id 0x010100C0B9283FC7311C488917E5A9876B01FC03

And you would delete the second child you would end up with an export:

  • Parent with id 0x010100C0B9283FC7311C488917E5A9876B01FC
  • Child 1 with id 0x010100C0B9283FC7311C488917E5A9876B01FC01
  • Child 3 with id 0x010100C0B9283FC7311C488917E5A9876B01FC03

Reproducing the error

Setting up a quick test with PowerShell allows you to reproduce the error. Just enter a set of content type id’s where a sub is missing. 

$parentCt = new-object Microsoft.SharePoint.Client.ContentTypeCreationInformation;
$parentCt.Name="Parent ContentType"
$parentCt.Description="Sample"
$parentCt.Id = "0x010100C0B9283FC7311C488917E5A9876B01FC"
$parentCt.Group= "Sample"
$ct = $ctCollection.Add($parentCt)

$ctChildOne = new-object Microsoft.SharePoint.Client.ContentTypeCreationInformation;
$ctChildOne.Name="Child ContentType 1"
$ctChildOne.Description="Sample"
$ctChildOne.Id = "0x010100C0B9283FC7311C488917E5A9876B01FC01"
$ctChildOne.Group= "Sample"
$ct = $ctCollection.Add($ctChildOne)

$ctChildThree = new-object Microsoft.SharePoint.Client.ContentTypeCreationInformation;
$ctChildThree.Name="Child ContentType 3"
$ctChildThree.Description="Sample"
$ctChildThree.Id = "0x010100C0B9283FC7311C488917E5A9876B01FC03"
$ctChildThree.Group= "Sample"
$ct = $ctCollection.Add($ctChildThree)

In the sample above the 02 ID is missing and you will see that if you create a Sub 4 content type through the UI you will encounter the error.The error will occur only if you create a new content type that derives from the Parent ContentType. While if you create a new content type that derives from the Child ContentType 3 everything will work as expected.

A solution

There are two approaches to fixing this issue. You can either identify the missing ID’s upfront and fix them in your import. By identifying the ID’s that are missing you can create content types with the required ID and delete them instantly. However if you are stuck with a tenant that has these issues you can also use a sample script to create ten content types based on the corrupted content type. This will update the NextChildByte by ten. If the problem still exists you can rerun the script again to create another ten content types.

Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"

$username=Read-Host -Prompt "UserName"
$pwd=Read-Host -Prompt "Password" -AsSecureString
$url=Read-Host -Prompt "URL Of site with ContentTypes"

function CreateChildContentTypes
{
    param($ct, $ctx)
    $ctID = $ct.ID.ToString()
    $ammount = 10;

    for($i=0;$i -lt $ammount; $i++)
    {
        #Create a new child content type with a unique guid and then delete it
        $newCTID = $ctID + "00" + [Guid]::NewGuid().ToString("n").ToUpper()
        $newCT = new-object Microsoft.SharePoint.Client.ContentTypeCreationInformation
        $newCT.Name="Empty CT "+ $i

        # Create
        $newCT.Id = $newCTID
        $newCT.Group= "Empty Content Types"
        $ct = $ctx.Web.ContentTypes.Add($newCT)
        $ctx.Load($ct);
        $ctx.ExecuteQuery();

        # Delete
        $ct = $ctx.Web.ContentTypes.GetById($ct.Id)
        $ct.DeleteObject();
        $ctx.ExecuteQuery();
    }

    Write-Host "Created $ammount child content types"
}


$ctx=New-Object Microsoft.SharePoint.Client.ClientContext($url)
$ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $pwd)

$web = $ctx.Web
$ct = $ctx.Web.AvailableContentTypes.GetById("0x010100C0B9283FC7311C488917E5A9876B01FC");
$ctx.Load($ct);
$ctx.Load($web);
$ctx.ExecuteQuery();

CreateChildContentTypes $ct $ctx
Loading comments…