Confusing duplicate content type error

Confusing duplicate content type error

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.

Duplicate Content Type 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.

Succesfully created child contenttype

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
There are 2 comments for this article
  1. Guðjón Örn Þorsteinsson at 18:41

    Inspired by this post. Use at will

    function ReserveChildContentTypeIds
    {

    param($Context, $parentCTID)
    $parentCt = Get-PnPContentType -Identity $parentCTID;
    $done = $false
    while ($done -eq $false) {
    $randomGuid = [Guid]::NewGuid().ToString("n").ToUpper()
    $tempCt = Add-PnPContentType -Name "Temp CT $randomGuid" -ParentContentType $parentCt -Group "Temp"
    $newIdPart = $tempCt.Id -replace $parentCt.Id, ""
    Write-Host "Reserving $newIdPart"
    Remove-PnPContentType -Identity $tempCt -Force
    if ($newIdPart -match "^00") {
    Write-Host "All numberd ids taken, no need to reserve any more"
    return
    }
    }
    }

    • Guðjón Örn Þorsteinsson at 18:43

      The documentation was removed. here it comes.

      .SYNOPSIS
      Reserve all short child Content Type IDs for a ContentType

      .DESCRIPTION
      Content types created by PnP export may have been created as short Id's
      If a user inherits a parent content type that was deployed using PnP he will
      automatically get the next Short ID available. This can create a conflict if
      the source site is exported and re-imported and the developer of the source
      site has added a new child content type. In that case the new CT will have
      the same ID as the one previously created by a user.

      This script will create and delete child content types to increment the next ID
      until all short (two character) ID's are taken. After that is complete any content
      type created by a user will be created with a 00[GUID] suffix.

      Any future content types imported with PnP are thus guarantied not to be in
      conflict even if the CT id's are of the short incremental variety.

      See: https://www.sharepointappie.nl/confusing-error-deploying-content-types/
      See: https://msdn.microsoft.com/en-us/library/office/aa543822(v=office.14).aspx

      .NOTES
      Author: Guðjón Örn Þorsteinsson
      Version: 1.0
      .EXAMPLE
      $context = Get-PnPContext
      ReserveChildContentTypeIds $context "0x0101009256F8ECD2AA5B4C8DE1BE558142E9B2"

Leave a Reply