Tuesday, April 22, 2014

Adding a content type to a list using CSOM in JavaScript throws “Collection has not been initialized” error

My colleague Janaka at Puzzlepart is writing a CSOM PowerShell script to add a content type to a custom list. Doing this he stumbled upon an interesting/weird error.
$clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)
$clientContext.Credentials = $credentials
$site = $clientContext.Site;
$web = $site.RootWeb;           
$ct = $web.ContentTypes.GetById("0x0100FE72F7843250314FB0ED7A98DCE072C1")
$clientContext.Load($site)
$clientContext.Load($web)
$clientContext.Load($ct)
$list = $web.Lists.GetByTitle("Test One")
$cts = $list.ContentTypes
$clientContext.Load($cts)
$cts.AddExistingContentType($ct)
$clientContext.ExecuteQuery()

Once you execute the above code in a function, line 12 will throw the following error and stop your script.

format-default : The collection has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested.
+ CategoryInfo          : NotSpecified: (:) [format-default], CollectionNotInitializedException
+ FullyQualifiedErrorId : Microsoft.SharePoint.Client.CollectionNotInitializedException,Microsoft.PowerShell.Comma   nds.FormatDefaultCommand


This means that ExecuteQuery will not execute and the content type will not be added. Adding a try/catch block will not help either as it will never enter the catch block your script still quits.

However, if you paste the code in a POSH console you will get the error, but once ExecuteQuery runs the content type is actually added. So what is really going on here?

I’m not going to say it was obvious to me right away, but after four hours, several cups of coffee, a lunch break and reading up on the documentation it was quite obvious.

Initially you would assume it’s the $cts collection which is not initialized and throwing the error, but this is not the case. Reading the MSDN docs on AddExistingContentType you see that it returns a content type, the content type instance you just added to the list. And this is the object which throws the error when PowerShell tries to print it in the console.

The screenshot below shows it quite clearly as there is no error until you try display the $ctReturn object.

image

The returned content type has not been loaded and initialized yet. We are still doing CSOM here and have to explicitly load our objects. The easy fix as shown in the screenshot is to switch out line 12 in the original code with the following line:

$ctReturn = $cts.AddExistingContentType($ct)

as it will not try to print out the returned object as it previously did, but instead assign it to a variable. If you want to access properties of the lists content type instance use add a load statement for the object:

$site = $clientContext.Site;
$web = $site.RootWeb;           
$ct = $web.ContentTypes.GetById("0x0100FE72F7843250314FB0ED7A98DCE072C1")
$clientContext.Load($site)
$clientContext.Load($web)
$clientContext.Load($ct)
$list = $web.Lists.GetByTitle("Test One")
$cts = $list.ContentTypes
$clientContext.Load($cts)
$ctReturn = $cts.AddExistingContentType($ct)
$clientContext.Load($ctReturn)
$clientContext.ExecuteQuery()

If you refuse to assign a variable and instead want to catch the error in your script and have it continue then add a trap statement before the AddExistingContentType line

trap{continue}
$cts.AddExistingContentType($ct)
$clientContext.ExecuteQuery()