Skip to the content Back to Top

Using ASP.NET AJAX extensively in my latest project I've been sporadically running into the Sys.WebForms.PageRequestManagerParserErrorException. It got to the point that I was contemplating ripping out ASP.NET AJAX completely until this known issue had been ironed out. The various causes for this error are mentioned many different places, but for some samples, go here, here, and here.

Quoting from Eilon Lipton's blog posting, this particular exception is very common and can be caused by any one of the following:

  1. Calls to Response.Write():
    By calling Response.Write() directly you are bypassing the normal rendering mechanism of ASP.NET controls. The bits you write are going straight out to the client without further processing (well, mostly...). This means that UpdatePanel can't encode the data in its special format.
  2. Response filters:
    Similar to Response.Write(), response filters can change the rendering in such a way that the UpdatePanel won't know.
  3. HttpModules:
    Again, the same deal as Response.Write() and response filters.
  4. Server trace is enabled:
    If I were going to implement trace again, I'd do it differently. Trace is effectively written out using Response.Write(), and as such messes up the special format that we use for UpdatePanel.
  5. Calls to Server.Transfer():
    Unfortunately, there's no way to detect that Server.Transfer() was called. This means that UpdatePanel can't do anything intelligent when someone calls Server.Transfer(). The response sent back to the client is the HTML markup from the page to which you transferred. Since its HTML and not the special format, it can't be parsed, and you get the error.

The problem was I wasn't doing any of the above (who uses Response.Write in an ASP.NET application these days?) and I was still sporadically encountering the error - a show stopping error I might add. An error that is popped up in a javascript warning box completely undecipherable to the end user leaving an empty/useless/castrated UpdatePanel in its wake. This of course leaves the end user feeling likewise empty/useless/castrated (to say nothing of the developer).

This post here indicates that there is a problem with the RoleMangerModule or any time you set a cookie to the response in an AJAX callback, which can only be solved by doing one of the following:

  1. Disable caching of cookies in the RoleManager. (yuck)
  2. Handle the Application's Error event to clear the error from the Context selectively (eek).
  3. Disable EventValidation in the web form.
    <%@ Page Language="C#" EnableEventValidation="false" %>
    (gulp)

None of the above are entirely reasonable solutions (especially the last two), and the worst part was that my test page was just a simple contact page that did not change/set roles or cookies, or response.write, or set anything in the session, and wasn't receiving any Unicode character input, or even requiring a user to be logged in, or writing anything to the trace, or anything beyond the basics. And still it blew up. But only occasionally.

In order to faithfully reproduce the error, I finally determined that it must have something to do with sessions as it would only occur if the app pool had recycled and all browser windows had been closed. So, based on one of the comments in one of the above posts, even though I'm not touching session on one of the problem pages, I tried a hack in one of the problem page's Page_Load:

Session["FixAJAXSysBug"] = true;

And lo and behold, we're good to go! So even though I am not using Session on the problem page it must be attempting to set the initial session cookie using the Update Panel callback. So the solution is to make sure the initial session is set before any Update Panel callback takes place. How this got through into production is beyond me.

So if you're sporadically encountering the Sys.Webforms.PageRequestManagerServerErrorException, it could be for any of the above reasons or the fact that your dog/cat/stuffed teddy bear is sitting too close to your monitor. But give the last one a try in every page utilizing AJAX if you're using sessions in your application.

UPDATE: If the problem pages aren't even using session, just turn session off for the page:

<%@ Page EnableSessionState="false" ... %>

Or better yet, set it in your base class to always be off, and turn it on for the pages where you need it on.

UPDATE II: Further developments.

The .NET Control class methods ResolveUrl and ResolveClientUrl both take a relative URL as a parameter and return relative URLs for client browser use. So what's the difference between them?

In simple terms, ResolveClientUrl returns a path relative to the current page, and ResolveUrl returns a path relative to the site root. Both methods are particularly useful when passing in a relative URL prefaced with the tilde (~) to indicate the application root.

Let's say we have an image in a layout directory, and a page which needs to pass a usable relative URL for the image to the client browser.

image http://www.andornot.com/layout/images/andornotLogo.gif

page http://www.andornot.com/Products/Default.aspx.

Here is what we would get if we called the two methods from our Default.aspx page.

Page.ResolveUrl("~/layout/images/andornotLogo.gif")
"/layout/images/andornotLogo.gif"
Page.ResolveClientUrl("~/layout/images/andornotLogo.gif")
"../layout/images/andornotLogo.gif"

There is more to it, of course, particularly since these are Control methods, not exclusively Page methods. It's pretty straightforward to see how paths relative to a page resolve, but not necessarily so obvious when the control is being instantiated in a UserControl or MasterPage.

ResolveUrl uses the Control.TemplateSourceDirectory property to do its job, and that property value is the virtual directory of the Page or UserControl that contains the current server control.

ResolveClientUrl returns a URL relative to the folder containing the source file in which the control is instantiated.

Virtually every web project you work on requires that you get the current page's complete URL. Years ago, I got tired of using non-type-safe ways of munging together various Request.ServerVariables to get it (anyone remember stuff like "If Request.ServerVariables("HTTPS") = "on" Then..."?) and with .NET there had to be a better way. Due to a dearth of non-Request.ServerVariables examples out there back then, it took trusty trial and error Response.Write tests and the MSDN help to settle on the following:

Request.Url.Scheme + Request.Url.SchemeDelimiter + Request.Url.Authority + Request.Url.PathAndQuery

While I never understood why I still had to parse these together (and I sporadically investigated alternatives), it nevertheless felt so much better than parsing together strings like "://" with icky stuff like Request.ServerVariables("SERVER_NAME").

But yes, there had to be an even better way: a way that resisted forgetfulness and the ensuing battle with more than vague intellisense descriptions. Since I'm slow, it was only today that I found it. My my, sometimes I do miss the obvious:

Request.Url.ToString()
returns the canonically unescaped form of the URI (i.e. "http://www.example.com:80//thick and thin.htm")

and/or

Request.Url.AbsoluteUri
Returns the canonically escaped form of the URI (i.e. http://www.example.com:80//thick%20and%20thin.htm)

And yes, I had tried out AbsoluteUri before (I must have, right?), but for some reason, I still missed it.

  1. Install .NET 1.1 and all service packs if not on the OS already.
  2. Open the IIS Manager and navigate to the server root. (Not a website root, above that - the machine name.) 
  3. Double-click "ISAPI and CGI restrictions."
  4. Add the v1.1 aspnet_isapi.dll to "Allowed" restrictions (it'll be at %Windows%\Microsoft.NET\Framework\v1.1.4322\aspnet_isapi.dll)
  5. Navigate to the website root in IIS manager and double-click "ISAPI Filters."
  6. Add the ASP.NET 1.1 ISAPI filter from %Windows%\Microsoft.NET\Framework\v1.1.4322\aspnet_filter.dll
  7. Go to the website root again in IIS manager and look at "Advanced Settings."
  8. Ensure the website is using an ASP.NET 1.1 Application Pool (Vista created one for me when I installed .NET 1.1 - you may need to create one yourself.)
  9. Done.
Categories

Let Us Help You!

We're Librarians - We Love to Help People