Did you ever try using a console application to programmatically activate a feature on a web and ran into the following error?
Microsoft.SharePoint.WebPartPages.WebPartPageUserException: This page has encountered a critical error. Contact your system administrator if this problem persists.
This doesn’t really give you much to go on. There is no InnerException object that gives you more information about what exactly is going on. I’ll first briefly outline what exactly I am trying to do here.
I have a page layout that contains several web part zones. This page layout is contained in a feature with scope “Site” and is deployed to the master page gallery upon activation. Now, I have another feature with scope “Web” that is stapled to the custom site definition we’re using. This feature takes care of creating an instance of that page layout and is supposed to add several web parts specified in a config file. The code for this is contained in a feature receiver. The web parts I’m adding are stored in *.webpart and *.dwp files, which are part of my SharePoint solution.
Now, this is where I’m running into the error mentioned in the beginning of this post. I am using the
SPLimitedWebPartManager.ImportWebPart() method with an
XmlReader to load the web part definition that should be imported. Whenever this method was hit (I was using the debugger to see what’s going on), it would throw an exception. Funny detail here is that the
ImportWebPart() method has an
out parameter that should contain the error message when something goes wrong. Strangely enough the exception I was running into, was not contained in the parameter, but thrown as an actual exception.
For those of you not interested in the outline of what took me an entire day to figure out, I will first describe what solved the error in my case. Thinking about it, I should have probably noticed this in the first place, but at first I didn’t connect the dots. Checking the event viewer, I noticed an error there:
Safe mode did not start successfully. Could not load file or assembly ‘Microsoft.SharePoint.ApplicationPages, Version=184.108.40.206, Culture=neutral, PublicKeyToken=71e9bce111e9429c’ or one of its dependencies. The system cannot find the file specified.
Initially I figured this to be related to the weird way my virtual machine with the development environment was behaving, but it turned out that this was exactly what was causing the problem. Since I am running the code from a console application and not within one of the SharePoint web application pools, I had of course also no access to the web application
"_app_bin" folder. Apparently the DLL mentioned in the error is not added to the Global Assembly Cache, but is stored in the aforementioned folder. So, when I would run my console application, it would of course not be able to load the file, because it doesn’t know that it should look in that folder. I copied the DLL to my console application’s
bin folder and it worked!
For anyone interested in how I figured this out without using the error message in the event log. The stack trace of the exception looked like this:
"Microsoft.SharePoint.WebPartPages.WebPartPageUserException: This page has encountered a critical error. Contact your system administrator if this problem persists. at Microsoft.SharePoint.ApplicationRuntime.SafeControls.RethrowExceptionIfNeeded() at Microsoft.SharePoint.ApplicationRuntime.SafeControls.IsSafeControl(Type type, String& unsafeErrorMessage) at Microsoft.SharePoint.WebPartPages.WebPartImporter.CreateWebPart(Boolean clearConnections) at Microsoft.SharePoint.WebPartPages.WebPartImporter.Import(SPWebPartManager manager, XmlReader reader, Boolean clearConnections, Uri webPartPageUri, SPWeb spWeb) at Microsoft.SharePoint.WebPartPages.WebPartImporter.Import(SPWebPartManager manager, XmlReader reader, Boolean clearConnections, SPWeb spWeb) at Microsoft.SharePoint.WebPartPages.SPWebPartManager.ImportWebPart(XmlReader reader, String& errorMessage) at Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager.ImportWebPart(XmlReader reader, String& errorMessage) Â at ConsoleTest.WebPartConfigurator.ConfigureWebPartsForPage(SPWeb web, String pageUrl, String webPartsLocation) in e:\Development\Sandbox\ConsoleTest\ConsoleTest\WebPartConfigurator.cs:line 68"
Since I wasn’t getting anything specific from the exception, I figured I’d start .NET Reflector to disassemble the
WebPartImporter.CreateWebPart() method to see where it would crash. Somewhere in that method the imported web part is being validated to see whether it’s actually a safe control, hence the call to
SafeControls object is an internal property of the
SPWeb object. When I opened the
SafeControls.IsSafeControls() method, I noticed that the last method in the call stack (where the exception is thrown) is actually
SafeControls.RethrowExceptionIfNeeded(). This appeared to be the first method that’s called in
SafeControls.IsSafeControls(). In this method, it checks to see if a private field called
_configException is not null. If not, it would throw the exception.
So I figured that since an exception already occured before it could actually check to see if the control is safe, I decided to see if I could find the place in the code where the exception was be thrown. Since SharePoint uses resource files for all of its text, I could not just search on the exception text. With Reflector, I opened
Microsoft.SharePoint.intl.dll and found the resource entry with my exception text, with key “SafeModeFailedInitialize”.
Since I had already concluded that the exception already occurred before doing validation, I tried to figure out where the actual list of safe controls is being built. Opening the constructor of the
SafeControls class showed the following:
this._configException = new WebPartPageUserException(SPResource.GetString("SafeModeFailedInitialize", new object));
It appeared that I had found the source of the problem! Now only to figure out why it would do that…
SafeControlsList object that’s being initialized there is trying to load the web application config file in order to parse the SafeControls section. At first I thought: “Maybe it cannot find the config file because I’m running the code from a console application”. However, there is already some code in place that looks for the config file if it’s not executed in the web application context, so it had to be something else.
Further down in the constructor, the method
InitSafeControlsInfoFromConfig() is called. Scrolling through the code I noticed the following line:
throw new WebPartPageUserException(SPResource.GetString("SafeModeFailedInitialize", new object));
Interesting, that’s exactly the same error message as I discovered earlier! I searched the code and found out that this exception is thrown when one of the assemblies in the SafeControls section is null. Then I remembered the error in the event log of which I didn’t think anything in the beginning. Adding things up, I quickly drew the conclusion that the missing DLL must be the source of my problem. So, I checked the Global Assembly Cache and couldn’t find the assembly there, which made sense, otherwise I wouldn’t be getting the error. Since the code was working when I activated the feature through the Site Settings page, the DLL had to be in the
_app_bin folder of the web application.
And indeed, the file was indeed in that folder. I copied the file to the
bin folder of my console application and tried to run it again. This time it completed without any exceptions!
It took me pretty much the entire day to figure out what was going on, that’s why I’m writing this blog post. Two reasons: I hope it helps anyone who’s running into the same problem and a brain dump for myself, should I ever run into the same issue again.