diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp
index d8d30037c8254218531bbd565269fc3850426dcb..087cfb8d481dde5ac8318cd77348c6b2ffaa8f14 100644
--- a/indra/newview/llaisapi.cpp
+++ b/indra/newview/llaisapi.cpp
@@ -741,8 +741,10 @@ void AISAPI::FetchCategoryLinks(const LLUUID &catId, completion_t callback)
         (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend),
         _1, _2, _3, _5, _6);
 
+    LLSD body;
+    body["depth"] = 0;
     LLCoprocedureManager::CoProcedure_t proc(
-        boost::bind(&AISAPI::InvokeAISCommandCoro, _1, getFn, url, LLUUID::null, LLSD(), callback, FETCHCATEGORYLINKS));
+        boost::bind(&AISAPI::InvokeAISCommandCoro, _1, getFn, url, LLUUID::null, body, callback, FETCHCATEGORYLINKS));
 
     EnqueueAISCommand("FetchCategoryLinks", proc);
 }
@@ -1337,13 +1339,6 @@ void AISUpdate::parseCategory(const LLSD& category_map, S32 depth)
         return;
     }
 
-	// Check descendent count first, as it may be needed
-	// to populate newly created categories
-	if (category_map.has("_embedded"))
-	{
-		parseDescendentCount(category_id, category_map["_embedded"]);
-	}
-
 	LLPointer<LLViewerInventoryCategory> new_cat;
 	if (curr_cat)
 	{
@@ -1366,6 +1361,13 @@ void AISUpdate::parseCategory(const LLSD& category_map, S32 depth)
 	// *NOTE: unpackMessage does not unpack version or descendent count.
 	if (rv)
 	{
+        // Check descendent count first, as it may be needed
+        // to populate newly created categories
+        if (category_map.has("_embedded"))
+        {
+            parseDescendentCount(category_id, new_cat->getPreferredType(), category_map["_embedded"]);
+        }
+
         if (mFetch)
         {
             uuid_int_map_t::const_iterator lookup_it = mCatDescendentsKnown.find(category_id);
@@ -1379,10 +1381,20 @@ void AISUpdate::parseCategory(const LLSD& category_map, S32 depth)
                 // set version only if we are sure this update has full data and embeded items
                 // since viewer uses version to decide if folder and content still need fetching
                 if (version > LLViewerInventoryCategory::VERSION_UNKNOWN
-                    && (depth >= 0 || (curr_cat && curr_cat->getVersion() > LLViewerInventoryCategory::VERSION_UNKNOWN)))
+                    && depth >= 0)
                 {
-                    LL_DEBUGS("Inventory") << "Setting version to " << version
-                        << " for category " << category_id << LL_ENDL;
+                    if (curr_cat && curr_cat->getVersion() > version)
+                    {
+                        LL_WARNS("Inventory") << "Version was " << curr_cat->getVersion()
+                            << ", but fetch returned version " << version
+                            << " for category " << category_id << LL_ENDL;
+                    }
+                    else
+                    {
+                        LL_DEBUGS("Inventory") << "Setting version to " << version
+                            << " for category " << category_id << LL_ENDL;
+                    }
+
                     new_cat->setVersion(version);
                 }
             }
@@ -1445,27 +1457,21 @@ void AISUpdate::parseCategory(const LLSD& category_map, S32 depth)
 	}
 }
 
-void AISUpdate::parseDescendentCount(const LLUUID& category_id, const LLSD& embedded)
+void AISUpdate::parseDescendentCount(const LLUUID& category_id, LLFolderType::EType type, const LLSD& embedded)
 {
-    if (mType == AISAPI::FETCHCOF)
+    // We can only determine true descendent count if this contains all descendent types.
+    if (embedded.has("categories") &&
+        embedded.has("links") &&
+        embedded.has("items"))
     {
-        // contains only links
-        if (embedded.has("links"))
-        {
-            mCatDescendentsKnown[category_id] = embedded["links"].size();
-        }
+        mCatDescendentsKnown[category_id] = embedded["categories"].size();
+        mCatDescendentsKnown[category_id] += embedded["links"].size();
+        mCatDescendentsKnown[category_id] += embedded["items"].size();
     }
-    else
+    else if (mFetch && embedded.has("links") && (type == LLFolderType::FT_CURRENT_OUTFIT || type == LLFolderType::FT_OUTFIT))
     {
-        // We can only determine true descendent count if this contains all descendent types.
-        if (embedded.has("categories") &&
-            embedded.has("links") &&
-            embedded.has("items"))
-        {
-            mCatDescendentsKnown[category_id] = embedded["categories"].size();
-            mCatDescendentsKnown[category_id] += embedded["links"].size();
-            mCatDescendentsKnown[category_id] += embedded["items"].size();
-        }
+        // COF and outfits contain links only
+        mCatDescendentsKnown[category_id] = embedded["links"].size();
     }
 }
 
diff --git a/indra/newview/llaisapi.h b/indra/newview/llaisapi.h
index 53c74ae0780ea4326524c46bccdbc598311cf39b..0fdf4a0b7421b84fc69d64cadee501057428c9f4 100644
--- a/indra/newview/llaisapi.h
+++ b/indra/newview/llaisapi.h
@@ -118,7 +118,7 @@ class AISUpdate
 	void parseLink(const LLSD& link_map, S32 depth);
 	void parseItem(const LLSD& link_map);
 	void parseCategory(const LLSD& link_map, S32 depth);
-	void parseDescendentCount(const LLUUID& category_id, const LLSD& embedded);
+	void parseDescendentCount(const LLUUID& category_id, LLFolderType::EType type, const LLSD& embedded);
 	void parseEmbedded(const LLSD& embedded, S32 depth);
 	void parseEmbeddedLinks(const LLSD& links, S32 depth);
 	void parseEmbeddedItems(const LLSD& items);
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index 80ef5e3bae7c2a80bbec728f67ee440ece3034ad..876ae23e62ac4c76708d53bf0e12d2f691bac5df 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -4446,7 +4446,7 @@ class CallAfterCategoryFetchStage1: public LLInventoryFetchDescendentsObserver
         {
             LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
             if (!cat) continue;
-            if (!isCategoryComplete(cat))
+            if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
             {
                 // CHECK IT: isCategoryComplete() checks both version and descendant count but
                 // fetch() only works for Unknown version and doesn't care about descentants,
@@ -4456,6 +4456,12 @@ class CallAfterCategoryFetchStage1: public LLInventoryFetchDescendentsObserver
                 cat->fetch();		//blindly fetch it without seeing if anything else is fetching it.
                 mIncomplete.push_back(*it);	//Add to list of things being downloaded for this observer.
             }
+            else if (!isCategoryComplete(cat))
+            {
+                LL_DEBUGS("Inventory") << "Categoty " << *it << " incomplete despite having version" << LL_ENDL;
+                LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch(*it, true);
+                mIncomplete.push_back(*it);
+            }
             else if (ais3)
             {
                 LLInventoryModel::cat_array_t* cats;
@@ -4484,10 +4490,7 @@ class CallAfterCategoryFetchStage1: public LLInventoryFetchDescendentsObserver
                     if (incomplete_count > MAX_INDIVIDUAL_FETCH
                         || (incomplete_count > 1 && complete_count == 0))
                     {
-                        // To prevent premature removal from mIncomplete and
-                        // since we are doing a full refetch anyway, mark unknown
-                        cat->setVersion(LLViewerInventoryCategory::VERSION_UNKNOWN);
-                        cat->fetch();
+                        LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch(*it, true);
                         mIncomplete.push_back(*it);
                     }
                     else
@@ -4496,6 +4499,7 @@ class CallAfterCategoryFetchStage1: public LLInventoryFetchDescendentsObserver
                         mComplete.push_back(*it);
                     }
                 }
+                // else should have been handled by isCategoryComplete
             }
             else
             {
@@ -4519,13 +4523,11 @@ class CallAfterCategoryFetchStage1: public LLInventoryFetchDescendentsObserver
 		// What we do here is get the complete information on the
 		// items in the requested category, and set up an observer
 		// that will wait for that to happen.
-		LLInventoryModel::cat_array_t cat_array;
-		LLInventoryModel::item_array_t item_array;
-		gInventory.collectDescendents(mComplete.front(),
-									  cat_array,
-									  item_array,
-									  LLInventoryModel::EXCLUDE_TRASH);
-		S32 count = item_array.size();
+        LLInventoryModel::cat_array_t* cats;
+        LLInventoryModel::item_array_t* items;
+        gInventory.getDirectDescendentsOf(mComplete.front(), cats, items);
+
+		S32 count = items->size();
 		if(!count)
 		{
 			LL_WARNS() << "Nothing fetched in category " << mComplete.front()
@@ -4537,11 +4539,13 @@ class CallAfterCategoryFetchStage1: public LLInventoryFetchDescendentsObserver
 			return;
 		}
 
-		LL_INFOS() << "stage1 got " << item_array.size() << " items, passing to stage2 " << LL_ENDL;
+        LLViewerInventoryCategory* cat = gInventory.getCategory(mComplete.front());
+        S32 version = cat ? cat->getVersion() : -2;
+		LL_INFOS() << "stage1, category " << mComplete.front() << " got " << count << " items, version " << version << " passing to stage2 " << LL_ENDL;
 		uuid_vec_t ids;
 		for(S32 i = 0; i < count; ++i)
 		{
-			ids.push_back(item_array.at(i)->getUUID());
+			ids.push_back(items->at(i)->getUUID());
 		}
 		
 		gInventory.removeObserver(this);
@@ -4570,14 +4574,14 @@ void callAfterCOFFetch(nullary_func_t cb)
 {
     LLUUID cat_id = LLAppearanceMgr::instance().getCOF();
     LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
-    if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
+
+    if (AISAPI::isAvailable())
     {
-        if (AISAPI::isAvailable())
-        {
-            // Mark cof (update timer) so that background fetch won't request it
-            cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE);
-            // Assume that we have no relevant cache. Fetch cof, and items cof's links point to.
-            AISAPI::FetchCOF([cb](const LLUUID& id)
+        // Mark cof (update timer) so that background fetch won't request it
+        cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE);
+        // For reliability assume that we have no relevant cache, so
+        // fetch cof along with items cof's links point to.
+        AISAPI::FetchCOF([cb](const LLUUID& id)
                          {
                              cb();
                              LLUUID cat_id = LLAppearanceMgr::instance().getCOF();
@@ -4587,18 +4591,12 @@ void callAfterCOFFetch(nullary_func_t cb)
                                  cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
                              }
                          });
-        }
-        else
-        {
-            LL_WARNS() << "AIS API v3 not available, can't use AISAPI::FetchCOF" << LL_ENDL;
-            // startup should have marked folder as fetching, remove that
-            cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
-            callAfterCategoryFetch(cat_id, cb);
-        }
     }
     else
     {
-        // Assume that cache is present. Process like a normal folder.
+        LL_INFOS() << "AIS API v3 not available, using callAfterCategoryFetch" << LL_ENDL;
+        // startup should have marked folder as fetching, remove that
+        cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
         callAfterCategoryFetch(cat_id, cb);
     }
 }
diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp
index bcd89cf8db068f03c110598c4b531dfe64449993..d3fce306bc7e3267c61b57a20478be81136d0480 100644
--- a/indra/newview/llattachmentsmgr.cpp
+++ b/indra/newview/llattachmentsmgr.cpp
@@ -243,6 +243,7 @@ void LLAttachmentsMgr::linkRecentlyArrivedAttachments()
         if (LLAppearanceMgr::instance().getCOFVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
         {
             // Wait for cof to load
+            LL_DEBUGS_ONCE("Avatar") << "Received atachments, but cof isn't loaded yet, postponing processing" << LL_ENDL;
             return;
         }
 
diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp
index 227d4285ebcfe3978e68b9f2fc8ff0179d510f28..51be44bef40cb9c752a3b9288ffd72134958bce6 100644
--- a/indra/newview/llinventoryobserver.cpp
+++ b/indra/newview/llinventoryobserver.cpp
@@ -334,21 +334,14 @@ void LLInventoryFetchItemsObserver::startFetch()
 
     if (aisv3)
     {
-        const S32 MAX_INDIVIDUAL_REQUESTS = 10;
+        const S32 MAX_INDIVIDUAL_REQUESTS = 7;
         for (requests_by_folders_t::value_type &folder : requests)
         {
-            LLViewerInventoryCategory* cat = gInventory.getCategory(folder.first);
             if (folder.second.size() > MAX_INDIVIDUAL_REQUESTS)
             {
                 // requesting one by one will take a while
                 // do whole folder
-                if (cat)
-                {
-                    // Either drop version or use scheduleFolderFetch to force-fetch
-                    // otherwise background fetch will ignore folders with set version
-                    cat->setVersion(LLViewerInventoryCategory::VERSION_UNKNOWN);
-                }
-                LLInventoryModelBackgroundFetch::getInstance()->start(folder.first);
+                LLInventoryModelBackgroundFetch::getInstance()->scheduleFolderFetch(folder.first, true);
             }
             else
             {
@@ -360,12 +353,11 @@ void LLInventoryFetchItemsObserver::startFetch()
                         // start fetching whole folder since it's not ready either way
                         cat->fetch();
                     }
-                    else if (cat->getViewerDescendentCount() <= folder.second.size())
+                    else if (cat->getViewerDescendentCount() <= folder.second.size()
+                             || cat->getDescendentCount() <= folder.second.size())
                     {
                         // Start fetching whole folder since we need all items
-                        // Drop version or use scheduleFolderFetch
-                        cat->setVersion(LLViewerInventoryCategory::VERSION_UNKNOWN);
-                        cat->fetch();
+                        LLInventoryModelBackgroundFetch::getInstance()->scheduleFolderFetch(folder.first, true);
 
                     }
                     else
@@ -382,6 +374,7 @@ void LLInventoryFetchItemsObserver::startFetch()
                     // Isn't supposed to happen? We should have all folders
                     // and if item exists, folder is supposed to exist as well.
                     llassert(false);
+                    LL_WARNS("Inventory") << "Missing folder: " << folder.first << " fetching items individually" << LL_ENDL;
 
                     // get items one by one
                     for (LLUUID &item_id : folder.second)