diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp
index 7241b3c0c28631f2d838981fd8be2e8f9dd9a73f..675da65af21d25a08bf83ff6ab53720028763b50 100644
--- a/indra/llinventory/llfoldertype.cpp
+++ b/indra/llinventory/llfoldertype.cpp
@@ -37,15 +37,22 @@
 struct FolderEntry : public LLDictionaryEntry
 {
 	FolderEntry(const std::string &type_name, // 8 character limit!
-				bool is_protected) // can the viewer change categories of this type?
+				bool is_protected, // can the viewer change categories of this type?
+				bool is_automatic, // always made before first login? 
+				bool is_singleton  // should exist as a unique copy under root
+		) 
 		:
 	LLDictionaryEntry(type_name),
-	mIsProtected(is_protected)
+	mIsProtected(is_protected),
+	mIsAutomatic(is_automatic),
+	mIsSingleton(is_singleton)
 	{
 		llassert(type_name.length() <= 8);
 	}
 
 	const bool mIsProtected;
+	const bool mIsAutomatic;
+	const bool mIsSingleton;
 };
 
 class LLFolderDictionary : public LLSingleton<LLFolderDictionary>,
@@ -59,50 +66,64 @@ class LLFolderDictionary : public LLSingleton<LLFolderDictionary>,
 	}
 };
 
+// Folder types
+// 
+// PROTECTED means that folders of this type can't be moved, deleted
+// or otherwise modified by the viewer.
+// 
+// SINGLETON means that there should always be exactly one folder of
+// this type, and it should be the root or a child of the root. This
+// is true for most types of folders.
+//
+// AUTOMATIC means that a copy of this folder should be created under
+// the root before the user ever logs in, and should never be created
+// from the viewer. A missing AUTOMATIC folder should be treated as a
+// fatal error by the viewer, since it indicates either corrupted
+// inventory or a failure in the inventory services.
+//
 LLFolderDictionary::LLFolderDictionary()
 {
-	//       													    TYPE NAME	PROTECTED
-	//      													   |-----------|---------|
-	addEntry(LLFolderType::FT_TEXTURE, 				new FolderEntry("texture",	TRUE));
-	addEntry(LLFolderType::FT_SOUND, 				new FolderEntry("sound",	TRUE));
-	addEntry(LLFolderType::FT_CALLINGCARD, 			new FolderEntry("callcard",	TRUE));
-	addEntry(LLFolderType::FT_LANDMARK, 			new FolderEntry("landmark",	TRUE));
-	addEntry(LLFolderType::FT_CLOTHING, 			new FolderEntry("clothing",	TRUE));
-	addEntry(LLFolderType::FT_OBJECT, 				new FolderEntry("object",	TRUE));
-	addEntry(LLFolderType::FT_NOTECARD, 			new FolderEntry("notecard",	TRUE));
-	addEntry(LLFolderType::FT_ROOT_INVENTORY, 		new FolderEntry("root_inv",	TRUE));
-	addEntry(LLFolderType::FT_LSL_TEXT, 			new FolderEntry("lsltext",	TRUE));
-	addEntry(LLFolderType::FT_BODYPART, 			new FolderEntry("bodypart",	TRUE));
-	addEntry(LLFolderType::FT_TRASH, 				new FolderEntry("trash",	TRUE));
-	addEntry(LLFolderType::FT_SNAPSHOT_CATEGORY, 	new FolderEntry("snapshot", TRUE));
-	addEntry(LLFolderType::FT_LOST_AND_FOUND, 		new FolderEntry("lstndfnd",	TRUE));
-	addEntry(LLFolderType::FT_ANIMATION, 			new FolderEntry("animatn",	TRUE));
-	addEntry(LLFolderType::FT_GESTURE, 				new FolderEntry("gesture",	TRUE));
-	addEntry(LLFolderType::FT_FAVORITE, 			new FolderEntry("favorite",	TRUE));
+	//       													    TYPE NAME, PROTECTED, AUTOMATIC, SINGLETON
+	addEntry(LLFolderType::FT_TEXTURE, 				new FolderEntry("texture",	TRUE, TRUE, TRUE));
+	addEntry(LLFolderType::FT_SOUND, 				new FolderEntry("sound",	TRUE, TRUE, TRUE));
+	addEntry(LLFolderType::FT_CALLINGCARD, 			new FolderEntry("callcard",	TRUE, TRUE, FALSE));
+	addEntry(LLFolderType::FT_LANDMARK, 			new FolderEntry("landmark",	TRUE, FALSE, FALSE));
+	addEntry(LLFolderType::FT_CLOTHING, 			new FolderEntry("clothing",	TRUE, TRUE, TRUE));
+	addEntry(LLFolderType::FT_OBJECT, 				new FolderEntry("object",	TRUE, TRUE, TRUE));
+	addEntry(LLFolderType::FT_NOTECARD, 			new FolderEntry("notecard",	TRUE, TRUE, TRUE));
+	addEntry(LLFolderType::FT_ROOT_INVENTORY, 		new FolderEntry("root_inv",	TRUE, TRUE, TRUE));
+	addEntry(LLFolderType::FT_LSL_TEXT, 			new FolderEntry("lsltext",	TRUE, TRUE, TRUE));
+	addEntry(LLFolderType::FT_BODYPART, 			new FolderEntry("bodypart",	TRUE, TRUE, TRUE));
+	addEntry(LLFolderType::FT_TRASH, 				new FolderEntry("trash",	TRUE, FALSE, TRUE));
+	addEntry(LLFolderType::FT_SNAPSHOT_CATEGORY, 	new FolderEntry("snapshot", TRUE, TRUE, TRUE));
+	addEntry(LLFolderType::FT_LOST_AND_FOUND, 		new FolderEntry("lstndfnd",	TRUE, TRUE, TRUE));
+	addEntry(LLFolderType::FT_ANIMATION, 			new FolderEntry("animatn",	TRUE, TRUE, TRUE));
+	addEntry(LLFolderType::FT_GESTURE, 				new FolderEntry("gesture",	TRUE, TRUE, TRUE));
+	addEntry(LLFolderType::FT_FAVORITE, 			new FolderEntry("favorite",	TRUE, FALSE, TRUE));
 	
 	for (S32 ensemble_num = S32(LLFolderType::FT_ENSEMBLE_START); ensemble_num <= S32(LLFolderType::FT_ENSEMBLE_END); ensemble_num++)
 	{
-		addEntry(LLFolderType::EType(ensemble_num), new FolderEntry("ensemble", FALSE)); 
+		addEntry(LLFolderType::EType(ensemble_num), new FolderEntry("ensemble", FALSE, FALSE, FALSE)); // Not used
 	}
 
-	addEntry(LLFolderType::FT_CURRENT_OUTFIT, 		new FolderEntry("current",	TRUE));
-	addEntry(LLFolderType::FT_OUTFIT, 				new FolderEntry("outfit",	FALSE));
-	addEntry(LLFolderType::FT_MY_OUTFITS, 			new FolderEntry("my_otfts",	TRUE));
+	addEntry(LLFolderType::FT_CURRENT_OUTFIT, 		new FolderEntry("current",	TRUE, FALSE, TRUE));
+	addEntry(LLFolderType::FT_OUTFIT, 				new FolderEntry("outfit",	FALSE, FALSE, FALSE));
+	addEntry(LLFolderType::FT_MY_OUTFITS, 			new FolderEntry("my_otfts",	TRUE, FALSE, TRUE));
 
-	addEntry(LLFolderType::FT_MESH, 				new FolderEntry("mesh",	TRUE));
+	addEntry(LLFolderType::FT_MESH, 				new FolderEntry("mesh",		TRUE, FALSE, FALSE)); // Not used?
 
-	addEntry(LLFolderType::FT_INBOX, 				new FolderEntry("inbox",	TRUE));
-	addEntry(LLFolderType::FT_OUTBOX, 				new FolderEntry("outbox",	TRUE));
+	addEntry(LLFolderType::FT_INBOX, 				new FolderEntry("inbox",	TRUE, FALSE, TRUE));
+	addEntry(LLFolderType::FT_OUTBOX, 				new FolderEntry("outbox",	TRUE, FALSE, FALSE));
 	
-	addEntry(LLFolderType::FT_BASIC_ROOT,			new FolderEntry("basic_rt", TRUE));
+	addEntry(LLFolderType::FT_BASIC_ROOT,			new FolderEntry("basic_rt", TRUE, FALSE, FALSE)); 
 
-	addEntry(LLFolderType::FT_MARKETPLACE_LISTINGS, new FolderEntry("merchant", FALSE));
-	addEntry(LLFolderType::FT_MARKETPLACE_STOCK,    new FolderEntry("stock",    FALSE));
-	addEntry(LLFolderType::FT_MARKETPLACE_VERSION,  new FolderEntry("version",    FALSE));
+	addEntry(LLFolderType::FT_MARKETPLACE_LISTINGS, new FolderEntry("merchant", FALSE, FALSE, FALSE));
+	addEntry(LLFolderType::FT_MARKETPLACE_STOCK,    new FolderEntry("stock",    FALSE, FALSE, FALSE));
+	addEntry(LLFolderType::FT_MARKETPLACE_VERSION,  new FolderEntry("version",  FALSE, FALSE, FALSE));
 		 
-    addEntry(LLFolderType::FT_SETTINGS,             new FolderEntry("settings", TRUE));
+    addEntry(LLFolderType::FT_SETTINGS,             new FolderEntry("settings", TRUE, FALSE, TRUE));
 
-	addEntry(LLFolderType::FT_NONE, 				new FolderEntry("-1",		FALSE));
+	addEntry(LLFolderType::FT_NONE, 				new FolderEntry("-1",		FALSE, FALSE, FALSE));
 };
 
 // static
@@ -126,8 +147,8 @@ const std::string &LLFolderType::lookup(LLFolderType::EType folder_type)
 }
 
 // static
-// Only ensembles and plain folders aren't protected.  "Protected" means
-// you can't change certain properties such as their type.
+// Only plain folders and a few other types aren't protected.  "Protected" means
+// you can't move, deleted, or change certain properties such as their type.
 bool LLFolderType::lookupIsProtectedType(EType folder_type)
 {
 	const LLFolderDictionary *dict = LLFolderDictionary::getInstance();
@@ -138,6 +159,32 @@ bool LLFolderType::lookupIsProtectedType(EType folder_type)
 	}
 	return true;
 }
+ 
+// static
+// Is this folder type automatically created outside the viewer? 
+bool LLFolderType::lookupIsAutomaticType(EType folder_type)
+{
+	const LLFolderDictionary *dict = LLFolderDictionary::getInstance();
+	const FolderEntry *entry = dict->lookup(folder_type);
+	if (entry)
+	{
+		return entry->mIsAutomatic;
+	}
+	return true;
+}
+
+// static
+// Should this folder always exist as a single copy under (or as) the root?
+bool LLFolderType::lookupIsSingletonType(EType folder_type)
+{
+	const LLFolderDictionary *dict = LLFolderDictionary::getInstance();
+	const FolderEntry *entry = dict->lookup(folder_type);
+	if (entry)
+	{
+		return entry->mIsSingleton;
+	}
+	return true;
+}
 
 // static
 bool LLFolderType::lookupIsEnsembleType(EType folder_type)
diff --git a/indra/llinventory/llfoldertype.h b/indra/llinventory/llfoldertype.h
index 85b86f9ce5452fe2be1a642233cd58703c9bbf84..1f174520dae476a74eea7b5dc6a7e627f4841168 100644
--- a/indra/llinventory/llfoldertype.h
+++ b/indra/llinventory/llfoldertype.h
@@ -102,6 +102,8 @@ class LL_COMMON_API LLFolderType
 	static const std::string&	lookup(EType folder_type);
 
 	static bool 				lookupIsProtectedType(EType folder_type);
+	static bool 				lookupIsAutomaticType(EType folder_type);
+	static bool 				lookupIsSingletonType(EType folder_type);
 	static bool 				lookupIsEnsembleType(EType folder_type);
 
 	static LLAssetType::EType	folderTypeToAssetType(LLFolderType::EType folder_type);
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 28db6a58084dee8de33250f8087e22c9c78d9bf3..1da5a717100f4c5e1c9c35299aa40f59cb0b172d 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -67,6 +67,9 @@
 #include "process.h"
 #endif
 
+#include <algorithm>
+#include <boost/algorithm/string/join.hpp>
+
 // Increment this if the inventory contents change in a non-backwards-compatible way.
 // For viewer 2, the addition of link items makes a pre-viewer-2 cache incorrect.
 const S32 LLInventoryModel::sCurrentInvCacheVersion = 2;
@@ -128,6 +131,27 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
 	return rv;
 }
 
+///----------------------------------------------------------------------------
+/// Class LLInventoryValidationInfo
+///----------------------------------------------------------------------------
+LLInventoryValidationInfo::LLInventoryValidationInfo():
+	mFatalErrorCount(0),
+	mWarningCount(0)
+{
+}
+
+void LLInventoryValidationInfo::toOstream(std::ostream& os) const
+{
+	os << "mFatalErrorCount " << mFatalErrorCount << " mWarningCount " << mWarningCount;
+}
+
+std::ostream& operator<<(std::ostream& os, const LLInventoryValidationInfo& v)
+{
+	v.toOstream(os);
+	return os;
+}
+
+
 ///----------------------------------------------------------------------------
 /// Class LLInventoryModel
 ///----------------------------------------------------------------------------
@@ -568,20 +592,29 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
 										   const std::string& pname,
 										   inventory_func_type callback)
 {
-	
 	LLUUID id;
 	if(!isInventoryUsable())
 	{
 		LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL;
+		// FIXME failing but still returning an id?
 		return id;
 	}
 
 	if(LLFolderType::lookup(preferred_type) == LLFolderType::badLookup())
 	{
 		LL_DEBUGS(LOG_INV) << "Attempt to create undefined category." << LL_ENDL;
+		// FIXME failing but still returning an id?
 		return id;
 	}
 
+	if (preferred_type != LLFolderType::FT_NONE)
+	{
+		// Ultimately this should only be done for non-singleton
+		// types. Requires back-end changes to guarantee that others
+		// already exist.
+		LL_WARNS(LOG_INV) << "Creating new system folder, type " << preferred_type << LL_ENDL;
+	}
+
 	id.generate();
 	std::string name = pname;
 	if(!pname.empty())
@@ -611,7 +644,7 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
 		request["message"] = "CreateInventoryCategory";
 		request["payload"] = body;
 
-		LL_DEBUGS(LOG_INV) << "create category request: " << ll_pretty_print_sd(request) << LL_ENDL;
+		LL_DEBUGS(LOG_INV) << "Creating category via request: " << ll_pretty_print_sd(request) << LL_ENDL;
         LLCoros::instance().launch("LLInventoryModel::createNewCategoryCoro",
             boost::bind(&LLInventoryModel::createNewCategoryCoro, this, url, body, callback));
 
@@ -623,6 +656,10 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
 		return LLUUID::null;
 	}
 
+	// FIXME this UDP code path needs to be removed. Requires
+	// reworking many of the callers to use callbacks rather than
+	// assuming instant success.
+
 	// Add the category to the internal representation
 	LLPointer<LLViewerInventoryCategory> cat =
 		new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID());
@@ -632,6 +669,8 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
 	accountForUpdate(update);
 	updateCategory(cat);
 
+	LL_DEBUGS(LOG_INV) << "Creating category via UDP message CreateInventoryFolder, type " << preferred_type << LL_ENDL;
+
 	// Create the category on the server. We do this to prevent people
 	// from munging their protected folders.
 	LLMessageSystem* msg = gMessageSystem;
@@ -2576,11 +2615,6 @@ void LLInventoryModel::buildParentChildMap()
 			// observers start firing.
 		}
 	}
-
-	if (!gInventory.validate())
-	{
-	 	LL_WARNS(LOG_INV) << "model failed validity check!" << LL_ENDL;
-	}
 }
 
 // Would normally do this at construction but that's too early
@@ -3701,57 +3735,84 @@ void LLInventoryModel::dumpInventory() const
 	LL_INFOS() << "\n**********************\nEnd Inventory Dump" << LL_ENDL;
 }
 
+std::string get_full_path(const LLInventoryObject *cat)
+{
+	std::vector<std::string> path_elts;
+	std::map<LLUUID,bool> visited;
+	while (cat != NULL && !visited[cat->getUUID()])
+	{
+		path_elts.push_back(cat->getName());
+		// avoid infinite loop in the unlikely event of a cycle
+		visited[cat->getUUID()] = true;
+		cat = gInventory.getObject(cat->getParentUUID());
+	}
+	std::stringstream s;
+	std::string delim("/");
+	std::reverse(path_elts.begin(), path_elts.end());
+	std::string result = "/" + boost::algorithm::join(path_elts, delim);
+	return result;
+}
+
 // Do various integrity checks on model, logging issues found and
-// returning an overall good/bad flag.
-bool LLInventoryModel::validate() const
+// returning an overall good/bad flag. 
+LLPointer<LLInventoryValidationInfo> LLInventoryModel::validate() const
 {
-	bool valid = true;
+	LLPointer<LLInventoryValidationInfo> validation_info = new LLInventoryValidationInfo;
+	S32 fatalities = 0;
+	S32 warnings = 0;
 
 	if (getRootFolderID().isNull())
 	{
-		LL_WARNS() << "no root folder id" << LL_ENDL;
-		valid = false;
+		LL_WARNS("Inventory") << "no root folder id" << LL_ENDL;
+		fatalities++;
 	}
 	if (getLibraryRootFolderID().isNull())
 	{
-		LL_WARNS() << "no root folder id" << LL_ENDL;
-		valid = false;
+		LL_WARNS("Inventory") << "no library root folder id" << LL_ENDL;
+		fatalities++;
 	}
 
 	if (mCategoryMap.size() + 1 != mParentChildCategoryTree.size())
 	{
 		// ParentChild should be one larger because of the special entry for null uuid.
-		LL_INFOS() << "unexpected sizes: cat map size " << mCategoryMap.size()
-				<< " parent/child " << mParentChildCategoryTree.size() << LL_ENDL;
-		valid = false;
+		LL_INFOS("Inventory") << "unexpected sizes: cat map size " << mCategoryMap.size()
+							  << " parent/child " << mParentChildCategoryTree.size() << LL_ENDL;
+		warnings++;
 	}
 	S32 cat_lock = 0;
 	S32 item_lock = 0;
 	S32 desc_unknown_count = 0;
 	S32 version_unknown_count = 0;
+
+	typedef std::map<LLFolderType::EType, S32> ft_count_map;
+	ft_count_map ft_counts_under_root;
+	ft_count_map ft_counts_elsewhere;
+	
+	// Loop over all categories and check.
 	for(cat_map_t::const_iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
 	{
 		const LLUUID& cat_id = cit->first;
 		const LLViewerInventoryCategory *cat = cit->second;
 		if (!cat)
 		{
-			LL_WARNS() << "invalid cat" << LL_ENDL;
-			valid = false;
+			LL_WARNS("Inventory") << "null cat" << LL_ENDL;
+			warnings++;
 			continue;
 		}
 		if (cat_id != cat->getUUID())
 		{
-			LL_WARNS() << "cat id/index mismatch " << cat_id << " " << cat->getUUID() << LL_ENDL;
-			valid = false;
+			LL_WARNS("Inventory") << "cat id/index mismatch " << cat_id << " " << cat->getUUID() << LL_ENDL;
+			warnings++;
 		}
 
 		if (cat->getParentUUID().isNull())
 		{
 			if (cat_id != getRootFolderID() && cat_id != getLibraryRootFolderID())
 			{
-				LL_WARNS() << "cat " << cat_id << " has no parent, but is not root ("
-						<< getRootFolderID() << ") or library root ("
-						<< getLibraryRootFolderID() << ")" << LL_ENDL;
+				LL_WARNS("Inventory") << "cat " << cat_id << " has no parent, but is not root ("
+									  << getRootFolderID() << ") or library root ("
+									  << getLibraryRootFolderID() << ")" << LL_ENDL;
+				warnings++;
 			}
 		}
 		cat_array_t* cats;
@@ -3759,8 +3820,8 @@ bool LLInventoryModel::validate() const
 		getDirectDescendentsOf(cat_id,cats,items);
 		if (!cats || !items)
 		{
-			LL_WARNS() << "invalid direct descendents for " << cat_id << LL_ENDL;
-			valid = false;
+			LL_WARNS("Inventory") << "invalid direct descendents for " << cat_id << LL_ENDL;
+			warnings++;
 			continue;
 		}
 		if (cat->getDescendentCount() == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
@@ -3769,12 +3830,12 @@ bool LLInventoryModel::validate() const
 		}
 		else if (cats->size() + items->size() != cat->getDescendentCount())
 		{
-			LL_WARNS() << "invalid desc count for " << cat_id << " name [" << cat->getName()
-					<< "] parent " << cat->getParentUUID()
-					<< " cached " << cat->getDescendentCount()
-					<< " expected " << cats->size() << "+" << items->size()
-					<< "=" << cats->size() +items->size() << LL_ENDL;
-			valid = false;
+			LL_WARNS("Inventory") << "invalid desc count for " << cat_id << " name [" << cat->getName()
+								  << "] parent " << cat->getParentUUID()
+								  << " cached " << cat->getDescendentCount()
+								  << " expected " << cats->size() << "+" << items->size()
+								  << "=" << cats->size() +items->size() << LL_ENDL;
+			warnings++;
 		}
 		if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
 		{
@@ -3794,8 +3855,8 @@ bool LLInventoryModel::validate() const
 
 			if (!item)
 			{
-				LL_WARNS() << "null item at index " << i << " for cat " << cat_id << LL_ENDL;
-				valid = false;
+				LL_WARNS("Inventory") << "null item at index " << i << " for cat " << cat_id << LL_ENDL;
+				warnings++;
 				continue;
 			}
 
@@ -3803,10 +3864,10 @@ bool LLInventoryModel::validate() const
 			
 			if (item->getParentUUID() != cat_id)
 			{
-				LL_WARNS() << "wrong parent for " << item_id << " found "
-						<< item->getParentUUID() << " expected " << cat_id
-						<< LL_ENDL;
-				valid = false;
+				LL_WARNS("Inventory") << "wrong parent for " << item_id << " found "
+									  << item->getParentUUID() << " expected " << cat_id
+									  << LL_ENDL;
+				warnings++;
 			}
 
 
@@ -3814,17 +3875,17 @@ bool LLInventoryModel::validate() const
 			item_map_t::const_iterator it = mItemMap.find(item_id);
 			if (it == mItemMap.end())
 			{
-				LL_WARNS() << "item " << item_id << " found as child of "
-						<< cat_id << " but not in top level mItemMap" << LL_ENDL;
-				valid = false;
+				LL_WARNS("Inventory") << "item " << item_id << " found as child of "
+									  << cat_id << " but not in top level mItemMap" << LL_ENDL;
+				warnings++;
 			}
 			else
 			{
 				LLViewerInventoryItem *top_item = it->second;
 				if (top_item != item)
 				{
-					LL_WARNS() << "item mismatch, item_id " << item_id
-							<< " top level entry is different, uuid " << top_item->getUUID() << LL_ENDL;
+					LL_WARNS("Inventory") << "item mismatch, item_id " << item_id
+										  << " top level entry is different, uuid " << top_item->getUUID() << LL_ENDL;
 				}
 			}
 
@@ -3833,19 +3894,19 @@ bool LLInventoryModel::validate() const
 			bool found = getObjectTopmostAncestor(item_id, topmost_ancestor_id);
 			if (!found)
 			{
-				LL_WARNS() << "unable to find topmost ancestor for " << item_id << LL_ENDL;
-				valid = false;
+				LL_WARNS("Inventory") << "unable to find topmost ancestor for " << item_id << LL_ENDL;
+				warnings++;
 			}
 			else
 			{
 				if (topmost_ancestor_id != getRootFolderID() &&
 					topmost_ancestor_id != getLibraryRootFolderID())
 				{
-					LL_WARNS() << "unrecognized top level ancestor for " << item_id
-							<< " got " << topmost_ancestor_id
-							<< " expected " << getRootFolderID()
-							<< " or " << getLibraryRootFolderID() << LL_ENDL;
-					valid = false;
+					LL_WARNS("Inventory") << "unrecognized top level ancestor for " << item_id
+										  << " got " << topmost_ancestor_id
+										  << " expected " << getRootFolderID()
+										  << " or " << getLibraryRootFolderID() << LL_ENDL;
+					warnings++;
 				}
 			}
 		}
@@ -3859,9 +3920,9 @@ bool LLInventoryModel::validate() const
 			getDirectDescendentsOf(parent_id,cats,items);
 			if (!cats)
 			{
-				LL_WARNS() << "cat " << cat_id << " name [" << cat->getName()
-						<< "] orphaned - no child cat array for alleged parent " << parent_id << LL_ENDL;
-				valid = false;
+				LL_WARNS("Inventory") << "cat " << cat_id << " name [" << cat->getName()
+									  << "] orphaned - no child cat array for alleged parent " << parent_id << LL_ENDL;
+				warnings++;
 			}
 			else
 			{
@@ -3877,27 +3938,56 @@ bool LLInventoryModel::validate() const
 				}
 				if (!found)
 				{
-					LL_WARNS() << "cat " << cat_id << " name [" << cat->getName()
-							<< "] orphaned - not found in child cat array of alleged parent " << parent_id << LL_ENDL;
+					LL_WARNS("Inventory") << "cat " << cat_id << " name [" << cat->getName()
+										  << "] orphaned - not found in child cat array of alleged parent " << parent_id << LL_ENDL;
+				}
+			}
+		}
+
+		// Update count of preferred types
+		LLFolderType::EType folder_type = cat->getPreferredType();
+		bool cat_is_in_library = false;
+		LLUUID topmost_id;
+		if (getObjectTopmostAncestor(cat->getUUID(),topmost_id) && topmost_id == getLibraryRootFolderID())
+		{
+			cat_is_in_library = true;
+		}
+		if (!cat_is_in_library)
+		{
+			if (getRootFolderID().notNull() && (cat->getUUID()==getRootFolderID() || cat->getParentUUID()==getRootFolderID()))
+			{
+				ft_counts_under_root[folder_type]++;
+				if (folder_type != LLFolderType::FT_NONE)
+				{
+					LL_DEBUGS("Inventory") << "Under root cat: " << get_full_path(cat) << " folder_type " << folder_type << LL_ENDL;
+				}
+			}
+			else
+			{
+				ft_counts_elsewhere[folder_type]++;
+				if (folder_type != LLFolderType::FT_NONE)
+				{
+					LL_DEBUGS("Inventory") << "Elsewhere cat: " << get_full_path(cat) << " folder_type " << folder_type << LL_ENDL;
 				}
 			}
 		}
 	}
 
+	// Loop over all items and check
 	for(item_map_t::const_iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
 	{
 		const LLUUID& item_id = iit->first;
 		LLViewerInventoryItem *item = iit->second;
 		if (item->getUUID() != item_id)
 		{
-			LL_WARNS() << "item_id " << item_id << " does not match " << item->getUUID() << LL_ENDL;
-			valid = false;
+			LL_WARNS("Inventory") << "item_id " << item_id << " does not match " << item->getUUID() << LL_ENDL;
+			warnings++;
 		}
 
 		const LLUUID& parent_id = item->getParentUUID();
 		if (parent_id.isNull())
 		{
-			LL_WARNS() << "item " << item_id << " name [" << item->getName() << "] has null parent id!" << LL_ENDL;
+			LL_WARNS("Inventory") << "item " << item_id << " name [" << item->getName() << "] has null parent id!" << LL_ENDL;
 		}
 		else
 		{
@@ -3906,8 +3996,8 @@ bool LLInventoryModel::validate() const
 			getDirectDescendentsOf(parent_id,cats,items);
 			if (!items)
 			{
-				LL_WARNS() << "item " << item_id << " name [" << item->getName()
-						<< "] orphaned - alleged parent has no child items list " << parent_id << LL_ENDL;
+				LL_WARNS("Inventory") << "item " << item_id << " name [" << item->getName()
+									  << "] orphaned - alleged parent has no child items list " << parent_id << LL_ENDL;
 			}
 			else
 			{
@@ -3922,8 +4012,8 @@ bool LLInventoryModel::validate() const
 				}
 				if (!found)
 				{
-					LL_WARNS() << "item " << item_id << " name [" << item->getName()
-							<< "] orphaned - not found as child of alleged parent " << parent_id << LL_ENDL;
+					LL_WARNS("Inventory") << "item " << item_id << " name [" << item->getName()
+										  << "] orphaned - not found as child of alleged parent " << parent_id << LL_ENDL;
 				}
 			}
 				
@@ -3938,30 +4028,30 @@ bool LLInventoryModel::validate() const
 			// Linked-to UUID should have back reference to this link.
 			if (!hasBacklinkInfo(link_id, target_id))
 			{
-				LL_WARNS() << "link " << item->getUUID() << " type " << item->getActualType()
-						<< " missing backlink info at target_id " << target_id
-						<< LL_ENDL;
+				LL_WARNS("Inventory") << "link " << item->getUUID() << " type " << item->getActualType()
+									  << " missing backlink info at target_id " << target_id
+									  << LL_ENDL;
 			}
 			// Links should have referents.
 			if (item->getActualType() == LLAssetType::AT_LINK && !target_item)
 			{
-				LL_WARNS() << "broken item link " << item->getName() << " id " << item->getUUID() << LL_ENDL;
+				LL_WARNS("Inventory") << "broken item link " << item->getName() << " id " << item->getUUID() << LL_ENDL;
 			}
 			else if (item->getActualType() == LLAssetType::AT_LINK_FOLDER && !target_cat)
 			{
-				LL_WARNS() << "broken folder link " << item->getName() << " id " << item->getUUID() << LL_ENDL;
+				LL_WARNS("Inventory") << "broken folder link " << item->getName() << " id " << item->getUUID() << LL_ENDL;
 			}
 			if (target_item && target_item->getIsLinkType())
 			{
-				LL_WARNS() << "link " << item->getName() << " references a link item "
-						<< target_item->getName() << " " << target_item->getUUID() << LL_ENDL;
+				LL_WARNS("Inventory") << "link " << item->getName() << " references a link item "
+									  << target_item->getName() << " " << target_item->getUUID() << LL_ENDL;
 			}
 
 			// Links should not have backlinks.
 			std::pair<backlink_mmap_t::const_iterator, backlink_mmap_t::const_iterator> range = mBacklinkMMap.equal_range(link_id);
 			if (range.first != range.second)
 			{
-				LL_WARNS() << "Link item " << item->getName() << " has backlinks!" << LL_ENDL;
+				LL_WARNS("Inventory") << "Link item " << item->getName() << " has backlinks!" << LL_ENDL;
 			}
 		}
 		else
@@ -3975,15 +4065,66 @@ bool LLInventoryModel::validate() const
 				LLViewerInventoryItem *link_item = getItem(link_id);
 				if (!link_item || !link_item->getIsLinkType())
 				{
-					LL_WARNS() << "invalid backlink from target " << item->getName() << " to " << link_id << LL_ENDL;
+					LL_WARNS("Inventory") << "invalid backlink from target " << item->getName() << " to " << link_id << LL_ENDL;
 				}
 			}
 		}
 	}
-	
+
+	// Check system folders
+	for (auto fit=ft_counts_under_root.begin(); fit != ft_counts_under_root.end(); ++fit)
+	{
+		LL_DEBUGS("Inventory") << "Folder type " << fit->first << " count " << fit->second << " under root" << LL_ENDL;
+	}
+	for (auto fit=ft_counts_elsewhere.begin(); fit != ft_counts_elsewhere.end(); ++fit)
+	{
+		LL_DEBUGS("Inventory") << "Folder type " << fit->first << " count " << fit->second << " elsewhere" << LL_ENDL;
+	}
+	for (S32 ft=LLFolderType::FT_TEXTURE; ft<LLFolderType::FT_COUNT; ft++)
+	{
+		LLFolderType::EType folder_type = static_cast<LLFolderType::EType>(ft);
+		if (LLFolderType::lookup(folder_type)==LLFolderType::badLookup())
+		{
+			continue;
+		}
+		bool is_automatic = LLFolderType::lookupIsAutomaticType(folder_type);
+		bool is_singleton = LLFolderType::lookupIsSingletonType(folder_type);
+		S32 count_under_root = ft_counts_under_root[folder_type];
+		S32 count_elsewhere = ft_counts_elsewhere[folder_type];
+		if (is_singleton)
+		{
+			if (count_under_root==0)
+			{
+				LL_WARNS("Inventory") << "Expected system folder type " << ft << " was not found under root" << LL_ENDL;
+				// Need to create, if allowed.
+				if (is_automatic)
+				{
+					LL_WARNS("Inventory") << "Cannot create system folder of type " << ft << LL_ENDL;
+					fatalities++;
+				}
+				else
+				{
+					// Can create, and will when needed.
+					warnings++;
+				}
+			}
+			else if (count_under_root > 1)
+			{
+				LL_WARNS("Inventory") << "System folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL;
+				fatalities++;
+			}
+			if (count_elsewhere > 0)
+			{
+				LL_WARNS("Inventory") << "Found " << count_elsewhere << " extra folders of type " << ft << " outside of root" << LL_ENDL;
+				warnings++;
+			}
+		}
+	}
+
+
 	if (cat_lock > 0 || item_lock > 0)
 	{
-		LL_INFOS() << "Found locks on some categories: sub-cat arrays "
+		LL_INFOS("Inventory") << "Found locks on some categories: sub-cat arrays "
 				<< cat_lock << ", item arrays " << item_lock << LL_ENDL;
 	}
 	if (desc_unknown_count != 0)
@@ -3992,12 +4133,17 @@ bool LLInventoryModel::validate() const
 	}
 	if (version_unknown_count != 0)
 	{
-		LL_INFOS() << "Found " << version_unknown_count << " cats with unknown version" << LL_ENDL;
+		LL_INFOS("Inventory") << "Found " << version_unknown_count << " cats with unknown version" << LL_ENDL;
 	}
 
-	LL_INFOS() << "Validate done, valid = " << (U32) valid << LL_ENDL;
+	// FIXME need to fail login and tell user to retry, contact support if problem persists.
+	bool valid = (fatalities + warnings == 0);
+	LL_INFOS("Inventory") << "Validate done, valid = " << (U32) valid << LL_ENDL;
+
+	validation_info->mFatalErrorCount = fatalities;
+	validation_info->mWarningCount = warnings;
 
-	return valid;
+	return validation_info; 
 }
 
 ///----------------------------------------------------------------------------
diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h
index a4326aaeed9708a41e836f05f4f6798f96ed89d3..687d2d497ca062c2076e1cefa5fd1413cc58f781 100644
--- a/indra/newview/llinventorymodel.h
+++ b/indra/newview/llinventorymodel.h
@@ -55,7 +55,21 @@ class LLInventoryCategory;
 class LLMessageSystem;
 class LLInventoryCollectFunctor;
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+///----------------------------------------------------------------------------
+/// LLInventoryValidationInfo
+///----------------------------------------------------------------------------
+class LLInventoryValidationInfo: public LLRefCount
+{
+public:
+	LLInventoryValidationInfo();
+	void toOstream(std::ostream& os) const;
+
+	S32 mFatalErrorCount;
+	S32 mWarningCount;
+};
+std::ostream& operator<<(std::ostream& s, const LLInventoryValidationInfo& v);
+
+///----------------------------------------------------------------------------
 // LLInventoryModel
 //
 // Represents a collection of inventory, and provides efficient ways to access 
@@ -63,7 +77,7 @@ class LLInventoryCollectFunctor;
 //   NOTE: This class could in theory be used for any place where you need 
 //   inventory, though it optimizes for time efficiency - not space efficiency, 
 //   probably making it inappropriate for use on tasks.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+///----------------------------------------------------------------------------
 class LLInventoryModel
 {
 	LOG_CLASS(LLInventoryModel);
@@ -656,7 +670,7 @@ class LLInventoryModel
 	//--------------------------------------------------------------------
 public:
 	void dumpInventory() const;
-	bool validate() const;
+	LLPointer<LLInventoryValidationInfo> validate() const;
 
 /**                    Miscellaneous
  **                                                                            **
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 17777c3cebd0ee8c472eea94ed7a0d3143b64e03..603f790815fa52eeed02c6b62ca7d20649a6a677 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -1807,6 +1807,13 @@ bool idle_startup()
 		// This method MUST be called before gInventory.findCategoryUUIDForType because of 
 		// gInventory.mIsAgentInvUsable is set to true in the gInventory.buildParentChildMap.
 		gInventory.buildParentChildMap();
+
+		// If inventory is unusable, need to flag this and
+		// bail out. In particular, must not trigger creation of new system
+		// categories.
+		LLPointer<LLInventoryValidationInfo> validation_info = gInventory.validate();
+		// FIXME add handling of unfixable corruption here - need to exit and get support to fix.
+		
 		gInventory.createCommonSystemCategories();
 
 		// It's debatable whether this flag is a good idea - sets all
@@ -1850,6 +1857,10 @@ bool idle_startup()
 		display_startup();
 		LLStartUp::setStartupState( STATE_MISC );
 		display_startup();
+
+		// Update status check after various system folders created.
+		validation_info = gInventory.validate();
+
 		return FALSE;
 	}