See the question and my original answer on StackOverflow

To debug these errors, I strongly recommend to enable traces (Trace="true" in the @page directive, or use a global setting in web.config) so you can see the server's control tree.

On a GET, the control tree is this (note the unique IDs that were generated automatically):

  HtmlHead
    + ctl05 : HtmlGenericControl (from your PreRender code)
    + ctl01 : Title (the <title> tag)
    + ctl02 : FavIcon (your favicon.ascx)
    + ctl04 : HtmlLink (from the 'Blue' theme)

  HtmlForm
    + ctl03 : ScriptManager

The root causes of the 'Failed To Load ViewState' error are:

  • you use the ViewState. This is an option that in many cases can be disabled.
  • you don't use fixed IDs (that's why the Visual Studio's IDE adds automatic but fixed IDs to controls all the time).
  • you add controls dynamically, late, in the tree, as it changes the head's control tree. Plus, you add it before others (AddAt).

The generated IDs follow ASP.NET's lifecycle order:

  1. Title (static)
  2. FavIcon (static)
  3. ScriptManager (Init)
  4. Theme (Init, after overrides)
  5. Dynamic controls (After Init in this case)

The ASP.NET ViewState engine is a tree serializer/deserializer. Each node in the tree has it's IDs, plus a "full ID" composed of its parent's IDs and its own ID. Of course, as soon as you change IDs between serialization and deserialization, all bets are off, the engine will detect it and raise the 'Failed to Load ViewState' error.

So, if you put Visible=false, you'll use the ViewState. If you remove it, you won't. When you don't use the ViewState yourself, there are less chances to get the 'Failed to load viewstate' error, but ASP.NET can use some ViewState on your behalf when you use all its features (Theme, etc.). In the case of Visible, it just means the control is there (and uses ViewState), but not rendered (it's Render Size is 0). But you'll see the problem with any other property that use the ViewState, it's not specific to the Visible property (you could also try this.ViewState["test"] = "whatever" ).

If you put <uc1:favicon runat="server"></uc1:favicon> elsewhere in the page, it will also work because it won't be in the Head control tree anymore, and won't interfere with Theme's link or your dynamic control.

For Theme and ScriptManager, it's just that when you play with them, it changes, or not, the IDs, and the system may detect it or not.

There is an infinite number of ways to break the ViewState. What's really difficult is it may seem to work when it shouldn't (for example as long as you don't use FavIcon's ViewState, you don't notice there is an issue with your code).