diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp
index 9031ea5b1befda0c76cc2204fe7205f9caa467ab..eb2c156ca555f80b9487c435629d062b7c260fb7 100644
--- a/indra/newview/llcompilequeue.cpp
+++ b/indra/newview/llcompilequeue.cpp
@@ -124,7 +124,7 @@ class LLQueuedScriptAssetUpload : public LLScriptAssetUpload
     LLQueuedScriptAssetUpload(LLUUID taskId, LLUUID itemId, LLUUID assetId, TargetType_t targetType,
             bool isRunning, std::string scriptName, LLUUID queueId, LLUUID exerienceId, taskUploadFinish_f finish) :
         LLScriptAssetUpload(taskId, itemId, targetType, isRunning, 
-            exerienceId, std::string(), finish),
+            exerienceId, std::string(), finish, nullptr),
         mScriptName(scriptName),
         mQueueId(queueId)
     {
diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp
index 2d5f3df6a1d0ff6958f3be57de09dfac67fad49c..903ca6035091370f925a607285612213fed5751a 100644
--- a/indra/newview/llmaterialeditor.cpp
+++ b/indra/newview/llmaterialeditor.cpp
@@ -347,6 +347,7 @@ LLMaterialEditor::LLMaterialEditor(const LLSD& key)
     , mRevertedChanges(0)
     , mExpectedUploadCost(0)
     , mUploadingTexturesCount(0)
+    , mUploadingTexturesFailure(false)
 {
     const LLInventoryItem* item = getItem();
     if (item)
@@ -1251,9 +1252,9 @@ bool LLMaterialEditor::saveIfNeeded()
 {
     if (mUploadingTexturesCount > 0)
     {
-        // upload already in progress
-        // wait until textures upload
-        // will retry saving on callback
+        // Upload already in progress, wait until
+        // textures upload will retry saving on callback.
+        // Also should prevent some failure-callbacks
         return true;
     }
 
@@ -1318,18 +1319,38 @@ bool LLMaterialEditor::updateInventoryItem(const std::string &buffer, const LLUU
         if (task_id.isNull() && !agent_url.empty())
         {
             uploadInfo = std::make_shared<LLBufferedAssetUploadInfo>(item_id, LLAssetType::AT_MATERIAL, buffer,
-                [](LLUUID itemId, LLUUID newAssetId, LLUUID newItemId, LLSD) {
-                LLMaterialEditor::finishInventoryUpload(itemId, newAssetId, newItemId);
-            });
+                [](LLUUID itemId, LLUUID newAssetId, LLUUID newItemId, LLSD)
+                {
+                    // done callback
+                    LLMaterialEditor::finishInventoryUpload(itemId, newAssetId, newItemId);
+                },
+                nullptr // failure callback
+                );
             url = agent_url;
         }
         else if (!task_id.isNull() && !task_url.empty())
         {
             LLUUID object_uuid(task_id);
             uploadInfo = std::make_shared<LLBufferedAssetUploadInfo>(task_id, item_id, LLAssetType::AT_MATERIAL, buffer,
-                [object_uuid](LLUUID itemId, LLUUID, LLUUID newAssetId, LLSD) {
-                LLMaterialEditor::finishTaskUpload(itemId, newAssetId, object_uuid);
-            });
+                [](LLUUID itemId, LLUUID task_id, LLUUID newAssetId, LLSD)
+                {
+                    // done callback
+                    LLMaterialEditor::finishTaskUpload(itemId, newAssetId, task_id);
+                },
+                [](LLUUID itemId, LLUUID task_id, LLSD response, std::string reason)
+                {
+                    // failure callback
+                    LLSD floater_key;
+                    floater_key["taskid"] = task_id;
+                    floater_key["itemid"] = itemId;
+                    LLMaterialEditor* me = LLFloaterReg::findTypedInstance<LLMaterialEditor>("material_editor", floater_key);
+                    if (me)
+                    {
+                        me->setEnabled(true);
+                    }
+                    return true;
+                }
+                );
             url = task_url;
         }
 
@@ -1394,11 +1415,15 @@ void LLMaterialEditor::createInventoryItem(const std::string &buffer, const std:
                 inv_item_id,
                 LLAssetType::AT_MATERIAL,
                 output,
-                [](LLUUID item_id, LLUUID new_asset_id, LLUUID new_item_id, LLSD response) {
-            LL_INFOS("Material") << "inventory item uploaded.  item: " << item_id << " asset: " << new_asset_id << " new_item_id: " << new_item_id << " response: " << response << LL_ENDL;
-            LLSD params = llsd::map("ASSET_ID", new_asset_id);
-            LLNotificationsUtil::add("MaterialCreated", params);
-        });
+                [](LLUUID item_id, LLUUID new_asset_id, LLUUID new_item_id, LLSD response)
+                {
+                    // done callback
+                    LL_INFOS("Material") << "inventory item uploaded.  item: " << item_id << " asset: " << new_asset_id << " new_item_id: " << new_item_id << " response: " << response << LL_ENDL;
+                    LLSD params = llsd::map("ASSET_ID", new_asset_id);
+                    LLNotificationsUtil::add("MaterialCreated", params);
+                },
+                nullptr // failure callback, floater already closed
+            );
 
         const LLViewerRegion* region = gAgent.getRegion();
         if (region)
@@ -1729,6 +1754,8 @@ void LLMaterialEditor::uploadMaterialFromFile(const std::string& filename, S32 i
     }
 
     // Todo: no point in loading whole editor
+    // This uses 'filename' to make sure multiple bulk uploads work
+    // instead of fighting for a single instance.
     LLMaterialEditor* me = (LLMaterialEditor*)LLFloaterReg::getInstance("material_editor", LLSD().with("filename", filename).with("index", LLSD::Integer(index)));
     me->loadMaterial(model_in, filename_lc, index, false);
     me->saveIfNeeded();
@@ -2012,21 +2039,6 @@ void LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback(const LLSD& notificati
     }
 }
 
-void LLMaterialEditor::loadFromGLTFMaterial(LLUUID &asset_id)
-{
-    if (asset_id.isNull())
-    {
-        LL_WARNS("MaterialEditor") << "Trying to open material with null id" << LL_ENDL;
-        return;
-    }
-    LLMaterialEditor* me = (LLMaterialEditor*)LLFloaterReg::getInstance("material_editor");
-    me->mMaterialName = LLTrans::getString("New Material");
-    me->setTitle(me->mMaterialName);
-    me->setFromGLTFMaterial(gGLTFMaterialList.getMaterial(asset_id));
-    me->openFloater();
-    me->setFocus(TRUE);
-}
-
 void LLMaterialEditor::loadMaterial(const tinygltf::Model &model_in, const std::string &filename_lc, S32 index, bool open_floater)
 {
     if (model_in.materials.size() <= index)
@@ -3008,6 +3020,16 @@ void LLMaterialEditor::saveTexture(LLImageJ2C* img, const std::string& name, con
 
     U32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost();
 
+    LLSD key = getKey();
+    std::function<bool(LLUUID itemId, LLSD response, std::string reason)> failed_upload([key](LLUUID assetId, LLSD response, std::string reason)
+    {
+        LLMaterialEditor* me = LLFloaterReg::findTypedInstance<LLMaterialEditor>("material_editor", key);
+        if (me)
+        {
+            me->setFailedToUploadTexture();
+        }
+        return true; // handled
+    });
 
     LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
         buffer,
@@ -3023,13 +3045,26 @@ void LLMaterialEditor::saveTexture(LLImageJ2C* img, const std::string& name, con
         LLFloaterPerms::getEveryonePerms("Uploads"),
         expected_upload_cost, 
         false,
-        cb));
+        cb,
+        failed_upload));
 
     upload_new_resource(uploadInfo);
 }
 
+void LLMaterialEditor::setFailedToUploadTexture()
+{
+    mUploadingTexturesFailure = true;
+    mUploadingTexturesCount--;
+    if (mUploadingTexturesCount == 0)
+    {
+        setEnabled(true);
+    }
+}
+
 S32 LLMaterialEditor::saveTextures()
 {
+    mUploadingTexturesFailure = false; // not supposed to get here if already uploading
+
     S32 work_count = 0;
     LLSD key = getKey(); // must be locally declared for lambda's capture to work
     if (mBaseColorTextureUploadId == getBaseColorId() && mBaseColorTextureUploadId.notNull())
@@ -3044,16 +3079,29 @@ S32 LLMaterialEditor::saveTextures()
                 if (response["success"].asBoolean())
                 {
                     me->setBaseColorId(newAssetId);
+
+                    // discard upload buffers once texture have been saved
+                    me->mBaseColorJ2C = nullptr;
+                    me->mBaseColorFetched = nullptr;
+                    me->mBaseColorTextureUploadId.setNull();
+
+                    me->mUploadingTexturesCount--;
+
+                    if (!me->mUploadingTexturesFailure)
+                    {
+                        // try saving
+                        me->saveIfNeeded();
+                    }
+                    else if (me->mUploadingTexturesCount == 0)
+                    {
+                        me->setEnabled(true);
+                    }
                 }
                 else
                 {
-                    // To make sure that we won't retry (some failures can cb immediately)
-                    me->setBaseColorId(LLUUID::null);
+                    // stop upload if possible, unblock and let user decide
+                    me->setFailedToUploadTexture();
                 }
-                me->mUploadingTexturesCount--;
-
-                // try saving
-                me->saveIfNeeded();
             }
         });
     }
@@ -3069,16 +3117,29 @@ S32 LLMaterialEditor::saveTextures()
                 if (response["success"].asBoolean())
                 {
                     me->setNormalId(newAssetId);
+
+                    // discard upload buffers once texture have been saved
+                    me->mNormalJ2C = nullptr;
+                    me->mNormalFetched = nullptr;
+                    me->mNormalTextureUploadId.setNull();
+
+                    me->mUploadingTexturesCount--;
+
+                    if (!me->mUploadingTexturesFailure)
+                    {
+                        // try saving
+                        me->saveIfNeeded();
+                    }
+                    else if (me->mUploadingTexturesCount == 0)
+                    {
+                        me->setEnabled(true);
+                    }
                 }
                 else
                 {
-                    me->setNormalId(LLUUID::null);
+                    // stop upload if possible, unblock and let user decide
+                    me->setFailedToUploadTexture();
                 }
-                me->setNormalId(newAssetId);
-                me->mUploadingTexturesCount--;
-
-                // try saving
-                me->saveIfNeeded();
             }
         });
     }
@@ -3094,15 +3155,29 @@ S32 LLMaterialEditor::saveTextures()
                 if (response["success"].asBoolean())
                 {
                     me->setMetallicRoughnessId(newAssetId);
+
+                    // discard upload buffers once texture have been saved
+                    me->mMetallicRoughnessJ2C = nullptr;
+                    me->mMetallicRoughnessFetched = nullptr;
+                    me->mMetallicTextureUploadId.setNull();
+
+                    me->mUploadingTexturesCount--;
+
+                    if (!me->mUploadingTexturesFailure)
+                    {
+                        // try saving
+                        me->saveIfNeeded();
+                    }
+                    else if (me->mUploadingTexturesCount == 0)
+                    {
+                        me->setEnabled(true);
+                    }
                 }
                 else
                 {
-                    me->setMetallicRoughnessId(LLUUID::null);
+                    // stop upload if possible, unblock and let user decide
+                    me->setFailedToUploadTexture();
                 }
-                me->mUploadingTexturesCount--;
-
-                // try saving
-                me->saveIfNeeded();
             }
         });
     }
@@ -3119,21 +3194,39 @@ S32 LLMaterialEditor::saveTextures()
                 if (response["success"].asBoolean())
                 {
                     me->setEmissiveId(newAssetId);
+
+                    // discard upload buffers once texture have been saved
+                    me->mEmissiveJ2C = nullptr;
+                    me->mEmissiveFetched = nullptr;
+                    me->mEmissiveTextureUploadId.setNull();
+
+                    me->mUploadingTexturesCount--;
+
+                    if (!me->mUploadingTexturesFailure)
+                    {
+                        // try saving
+                        me->saveIfNeeded();
+                    }
+                    else if (me->mUploadingTexturesCount == 0)
+                    {
+                        me->setEnabled(true);
+                    }
                 }
                 else
                 {
-                    me->setEmissiveId(LLUUID::null);
+                    // stop upload if possible, unblock and let user decide
+                    me->setFailedToUploadTexture();
                 }
-                me->mUploadingTexturesCount--;
-
-                // try saving
-                me->saveIfNeeded();
             }
         });
     }
 
-    // discard upload buffers once textures have been saved
-    clearTextures();
+    if (!work_count)
+    {
+        // Discard upload buffers once textures have been confirmed as saved.
+        // Otherwise we keep buffers for potential upload failure recovery.
+        clearTextures();
+    }
 
     // asset storage can callback immediately, causing a decrease
     // of mUploadingTexturesCount, report amount of work scheduled
diff --git a/indra/newview/llmaterialeditor.h b/indra/newview/llmaterialeditor.h
index d23a741e49a509e4ab64409f893927de75766723..74c776031e5c7951c5bc79b14ab5ce7aabab3779 100644
--- a/indra/newview/llmaterialeditor.h
+++ b/indra/newview/llmaterialeditor.h
@@ -116,14 +116,13 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
     static void savePickedMaterialAs();
     static void onSaveObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response);
 
-    static void loadFromGLTFMaterial(LLUUID &asset_id);
-
     static void onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status);
 
     void inventoryChanged(LLViewerObject* object, LLInventoryObject::object_list_t* inventory, S32 serial_num, void* user_data) override;
 
     typedef std::function<void(LLUUID newAssetId, LLSD response)> upload_callback_f;
     void saveTexture(LLImageJ2C* img, const std::string& name, const LLUUID& asset_id, upload_callback_f cb);
+    void setFailedToUploadTexture();
 
     // save textures to inventory if needed
     // returns amount of scheduled uploads
@@ -295,6 +294,7 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
     U32 mRevertedChanges; // flags to indicate individual reverted parameters
     S32 mUploadingTexturesCount;
     S32 mExpectedUploadCost;
+    bool mUploadingTexturesFailure;
     std::string mMaterialNameShort;
     std::string mMaterialName;
 
diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp
index 759e7859f24317c996313c4ad0756d4b139cf5c0..544ff8b5dc4ea58fdf48863dbcd85e420212a3f0 100644
--- a/indra/newview/llpreviewgesture.cpp
+++ b/indra/newview/llpreviewgesture.cpp
@@ -1108,14 +1108,16 @@ void LLPreviewGesture::saveIfNeeded()
                 item->setComplete(true);
 
                 uploadInfo = std::make_shared<LLBufferedAssetUploadInfo>(mItemUUID, LLAssetType::AT_GESTURE, buffer,
-                    [](LLUUID itemId, LLUUID newAssetId, LLUUID, LLSD) {
+                    [](LLUUID itemId, LLUUID newAssetId, LLUUID, LLSD)
+                    {
                         LLPreviewGesture::finishInventoryUpload(itemId, newAssetId);
-                    });
+                    },
+                    nullptr);
                 url = agent_url;
             }
             else if (!mObjectUUID.isNull() && !task_url.empty())
             {
-                uploadInfo = std::make_shared<LLBufferedAssetUploadInfo>(mObjectUUID, mItemUUID, LLAssetType::AT_GESTURE, buffer, nullptr);
+                uploadInfo = std::make_shared<LLBufferedAssetUploadInfo>(mObjectUUID, mItemUUID, LLAssetType::AT_GESTURE, buffer, nullptr, nullptr);
                 url = task_url;
             }
 
diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp
index 3fd4f51559e023af6be25256548f039e97e8b031..2eccc0474ff02b6484d3bdff4e3c28efe488ec43 100644
--- a/indra/newview/llpreviewnotecard.cpp
+++ b/indra/newview/llpreviewnotecard.cpp
@@ -552,7 +552,8 @@ bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem, bool sync)
                     uploadInfo = std::make_shared<LLBufferedAssetUploadInfo>(mItemUUID, LLAssetType::AT_NOTECARD, buffer, 
                         [](LLUUID itemId, LLUUID newAssetId, LLUUID newItemId, LLSD) {
                             LLPreviewNotecard::finishInventoryUpload(itemId, newAssetId, newItemId);
-                        });
+                        },
+                        nullptr);
                     url = agent_url;
                 }
                 else if (!mObjectUUID.isNull() && !task_url.empty())
@@ -561,7 +562,8 @@ bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem, bool sync)
                     uploadInfo = std::make_shared<LLBufferedAssetUploadInfo>(mObjectUUID, mItemUUID, LLAssetType::AT_NOTECARD, buffer, 
                         [object_uuid](LLUUID itemId, LLUUID, LLUUID newAssetId, LLSD) {
                             LLPreviewNotecard::finishTaskUpload(itemId, newAssetId, object_uuid);
-                        });
+                        },
+                        nullptr);
                     url = task_url;
                 }
 
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp
index 5043250e0844ad71884e94091387081517aeaa8f..8b93dd103df51523b3a31fae9db417e36d018c67 100644
--- a/indra/newview/llpreviewscript.cpp
+++ b/indra/newview/llpreviewscript.cpp
@@ -1686,6 +1686,32 @@ void LLPreviewLSL::finishedLSLUpload(LLUUID itemId, LLSD response)
     }
 }
 
+bool LLPreviewLSL::failedLSLUpload(LLUUID itemId, LLUUID taskId, LLSD response, std::string reason)
+{
+    LLSD floater_key;
+    if (taskId.notNull())
+    {
+        floater_key["taskid"] = taskId;
+        floater_key["itemid"] = itemId;
+    }
+    else
+    {
+        floater_key = LLSD(itemId);
+    }
+
+    LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", floater_key);
+    if (preview)
+    {
+        // unfreeze floater
+        LLSD errors;
+        errors.append(LLTrans::getString("UploadFailed") + reason);
+        preview->callbackLSLCompileFailed(errors);
+        return true;
+    }
+
+    return false;
+}
+
 // Save needs to compile the text in the buffer. If the compile
 // succeeds, then save both assets out to the database. If the compile
 // fails, go ahead and save the text anyway.
@@ -1723,7 +1749,8 @@ void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/)
                 [old_asset_id](LLUUID itemId, LLUUID, LLUUID, LLSD response) {
                     LLFileSystem::removeFile(old_asset_id, LLAssetType::AT_LSL_TEXT);
                     LLPreviewLSL::finishedLSLUpload(itemId, response);
-                }));
+                },
+                LLPreviewLSL::failedLSLUpload));
 
             LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo);
         }
@@ -2282,7 +2309,8 @@ void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/)
                 [isRunning, old_asset_id](LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response) { 
                         LLFileSystem::removeFile(old_asset_id, LLAssetType::AT_LSL_TEXT);
                         LLLiveLSLEditor::finishLSLUpload(itemId, taskId, newAssetId, response, isRunning);
-                }));
+                },
+                nullptr)); // needs failure handling?
 
         LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo);
     }
diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h
index f851ff6f3f2547bf3723753f5b9c6c76091dc190..b01c7fd4ad291e98fcd446654eb18abb086d8031 100644
--- a/indra/newview/llpreviewscript.h
+++ b/indra/newview/llpreviewscript.h
@@ -245,6 +245,7 @@ class LLPreviewLSL : public LLScriptEdContainer
 	static void* createScriptEdPanel(void* userdata);
 
     static void finishedLSLUpload(LLUUID itemId, LLSD response);
+    static bool failedLSLUpload(LLUUID itemId, LLUUID taskId, LLSD response, std::string reason);
 protected:
 
 	// Can safely close only after both text and bytecode are uploaded
diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp
index f9b7c749b36322e35ba1afb5f1e23803dcfad620..59cfb4f0c467fed54049964960880bc6b5074d75 100644
--- a/indra/newview/llsettingsvo.cpp
+++ b/indra/newview/llsettingsvo.cpp
@@ -234,9 +234,11 @@ void LLSettingsVOBase::updateInventoryItem(const LLSettingsBase::ptr_t &settings
     LLSDSerialize::serialize(settingdata, buffer, LLSDSerialize::LLSD_NOTATION);
 
     LLResourceUploadInfo::ptr_t uploadInfo = std::make_shared<LLBufferedAssetUploadInfo>(inv_item_id, LLAssetType::AT_SETTINGS, buffer.str(), 
-        [settings, callback](LLUUID itemId, LLUUID newAssetId, LLUUID newItemId, LLSD response) {
+        [settings, callback](LLUUID itemId, LLUUID newAssetId, LLUUID newItemId, LLSD response)
+        {
             LLSettingsVOBase::onAgentAssetUploadComplete(itemId, newAssetId, newItemId, response, settings, callback);
-        });
+        },
+        nullptr);
 
     LLViewerAssetUpload::EnqueueInventoryUpload(agent_url, uploadInfo);
 }
@@ -265,9 +267,11 @@ void LLSettingsVOBase::updateInventoryItem(const LLSettingsBase::ptr_t &settings
     LLSDSerialize::serialize(settingdata, buffer, LLSDSerialize::LLSD_NOTATION);
 
     LLResourceUploadInfo::ptr_t uploadInfo = std::make_shared<LLBufferedAssetUploadInfo>(object_id, inv_item_id, LLAssetType::AT_SETTINGS, buffer.str(),
-        [settings, callback](LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response) {
-        LLSettingsVOBase::onTaskAssetUploadComplete(itemId, taskId, newAssetId, response, settings, callback);
-    });
+        [settings, callback](LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response)
+        {
+            LLSettingsVOBase::onTaskAssetUploadComplete(itemId, taskId, newAssetId, response, settings, callback);
+        },
+        nullptr);
 
     LLViewerAssetUpload::EnqueueInventoryUpload(agent_url, uploadInfo);
 }
diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp
index 3eb9f9eda2403fc6c569f55b9873eb7da9570637..f86c1405a79813dfb6209b8f0edc4b558a8947c2 100644
--- a/indra/newview/llviewerassetupload.cpp
+++ b/indra/newview/llviewerassetupload.cpp
@@ -34,7 +34,6 @@
 #include "lluuid.h"
 #include "llvorbisencode.h"
 #include "lluploaddialog.h"
-#include "llpreviewscript.h"
 #include "llnotificationsutil.h"
 #include "llagent.h"
 #include "llfloaterreg.h"
@@ -568,12 +567,14 @@ LLNewBufferedResourceUploadInfo::LLNewBufferedResourceUploadInfo(
     U32 everyonePerms,
     S32 expectedCost,
     bool show_inventory,
-    uploadFinish_f finish)
+    uploadFinish_f finish,
+    uploadFailure_f failure)
     : LLResourceUploadInfo(name, description, compressionInfo,
         destinationType, inventoryType,
         nextOWnerPerms, groupPerms, everyonePerms, expectedCost, show_inventory)
     , mBuffer(buffer)
     , mFinishFn(finish)
+    , mFailureFn(failure)
 {
     setAssetType(assetType);
     setAssetId(asset_id);
@@ -614,8 +615,18 @@ LLUUID LLNewBufferedResourceUploadInfo::finishUpload(LLSD &result)
     return newItemId;
 }
 
+bool LLNewBufferedResourceUploadInfo::failedUpload(LLSD &result, std::string &reason)
+{
+    if (mFailureFn)
+    {
+        return mFailureFn(getAssetId(), result, reason);
+    }
+
+    return false; // Not handled
+}
+
 //=========================================================================
-LLBufferedAssetUploadInfo::LLBufferedAssetUploadInfo(LLUUID itemId, LLAssetType::EType assetType, std::string buffer, invnUploadFinish_f finish) :
+LLBufferedAssetUploadInfo::LLBufferedAssetUploadInfo(LLUUID itemId, LLAssetType::EType assetType, std::string buffer, invnUploadFinish_f finish, uploadFailed_f failed) :
     LLResourceUploadInfo(std::string(), std::string(), 0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
         0, 0, 0, 0),
     mTaskUpload(false),
@@ -623,6 +634,7 @@ LLBufferedAssetUploadInfo::LLBufferedAssetUploadInfo(LLUUID itemId, LLAssetType:
     mContents(buffer),
     mInvnFinishFn(finish),
     mTaskFinishFn(nullptr),
+    mFailureFn(failed),
     mStoredToCache(false)
 {
     setItemId(itemId);
@@ -637,6 +649,7 @@ LLBufferedAssetUploadInfo::LLBufferedAssetUploadInfo(LLUUID itemId, LLPointer<LL
     mContents(),
     mInvnFinishFn(finish),
     mTaskFinishFn(nullptr),
+    mFailureFn(nullptr),
     mStoredToCache(false)
 {
     setItemId(itemId);
@@ -663,7 +676,7 @@ LLBufferedAssetUploadInfo::LLBufferedAssetUploadInfo(LLUUID itemId, LLPointer<LL
     mContents.assign((char *)image->getData(), imageSize);
 }
 
-LLBufferedAssetUploadInfo::LLBufferedAssetUploadInfo(LLUUID taskId, LLUUID itemId, LLAssetType::EType assetType, std::string buffer, taskUploadFinish_f finish) :
+LLBufferedAssetUploadInfo::LLBufferedAssetUploadInfo(LLUUID taskId, LLUUID itemId, LLAssetType::EType assetType, std::string buffer, taskUploadFinish_f finish, uploadFailed_f failed) :
     LLResourceUploadInfo(std::string(), std::string(), 0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
         0, 0, 0, 0),
     mTaskUpload(true),
@@ -671,6 +684,7 @@ LLBufferedAssetUploadInfo::LLBufferedAssetUploadInfo(LLUUID taskId, LLUUID itemI
     mContents(buffer),
     mInvnFinishFn(nullptr),
     mTaskFinishFn(finish),
+    mFailureFn(failed),
     mStoredToCache(false)
 {
     setItemId(itemId);
@@ -760,10 +774,19 @@ LLUUID LLBufferedAssetUploadInfo::finishUpload(LLSD &result)
     return newAssetId;
 }
 
+bool LLBufferedAssetUploadInfo::failedUpload(LLSD &result, std::string &reason)
+{
+    if (mFailureFn)
+    {
+        return mFailureFn(getItemId(), getTaskId(), result, reason);
+    }
+    return false;
+}
+
 //=========================================================================
 
-LLScriptAssetUpload::LLScriptAssetUpload(LLUUID itemId, std::string buffer, invnUploadFinish_f finish):
-    LLBufferedAssetUploadInfo(itemId, LLAssetType::AT_LSL_TEXT, buffer, finish),
+LLScriptAssetUpload::LLScriptAssetUpload(LLUUID itemId, std::string buffer, invnUploadFinish_f finish, uploadFailed_f failed):
+    LLBufferedAssetUploadInfo(itemId, LLAssetType::AT_LSL_TEXT, buffer, finish, failed),
     mExerienceId(),
     mTargetType(LSL2),
     mIsRunning(false)
@@ -771,8 +794,8 @@ LLScriptAssetUpload::LLScriptAssetUpload(LLUUID itemId, std::string buffer, invn
 }
 
 LLScriptAssetUpload::LLScriptAssetUpload(LLUUID taskId, LLUUID itemId, TargetType_t targetType,
-        bool isRunning, LLUUID exerienceId, std::string buffer, taskUploadFinish_f finish):
-    LLBufferedAssetUploadInfo(taskId, itemId, LLAssetType::AT_LSL_TEXT, buffer, finish),
+        bool isRunning, LLUUID exerienceId, std::string buffer, taskUploadFinish_f finish, uploadFailed_f failed):
+    LLBufferedAssetUploadInfo(taskId, itemId, LLAssetType::AT_LSL_TEXT, buffer, finish, failed),
     mExerienceId(exerienceId),
     mTargetType(targetType),
     mIsRunning(isRunning)
@@ -986,19 +1009,15 @@ void LLViewerAssetUpload::HandleUploadError(LLCore::HttpStatus status, LLSD &res
 
     LLNotificationsUtil::add(label, args);
 
-    // unfreeze script preview
-    if (uploadInfo->getAssetType() == LLAssetType::AT_LSL_TEXT)
+    if (uploadInfo->failedUpload(result, reason))
     {
-        LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", 
-            uploadInfo->getItemId());
-        if (preview)
-        {
-            LLSD errors;
-            errors.append(LLTrans::getString("UploadFailed") + reason);
-            preview->callbackLSLCompileFailed(errors);
-        }
+        // no further action required, already handled by a callback
+        // ex: do not trigger snapshot floater when failing material texture
+        return;
     }
 
+    // Todo: move these floater specific actions into proper callbacks
+
     // Let the Snapshot floater know we have failed uploading.
     LLFloaterSnapshot* floater_snapshot = LLFloaterSnapshot::findInstance();
     if (floater_snapshot && floater_snapshot->isWaitingState())
diff --git a/indra/newview/llviewerassetupload.h b/indra/newview/llviewerassetupload.h
index 9eddcfbd0e9525368aded554a1e769c42009ffa1..7f7707f5bb2f62d063a68e430bf86883cfb80aac 100644
--- a/indra/newview/llviewerassetupload.h
+++ b/indra/newview/llviewerassetupload.h
@@ -64,6 +64,9 @@ class LLResourceUploadInfo
     virtual void        logPreparedUpload();
     virtual LLUUID      finishUpload(LLSD &result);
 
+    // return true if no further action is need
+    virtual bool        failedUpload(LLSD &result, std::string &reason) { return false; }
+
     LLTransactionID     getTransactionId() const { return mTransactionId; }
     LLAssetType::EType  getAssetType() const { return mAssetType; }
     std::string         getAssetTypeString() const;
@@ -173,6 +176,7 @@ class LLNewBufferedResourceUploadInfo : public LLResourceUploadInfo
 {
 public:
     typedef std::function<void(LLUUID newAssetId, LLSD response)> uploadFinish_f;
+    typedef std::function<bool(LLUUID assetId, LLSD response, std::string reason)> uploadFailure_f;
 
     LLNewBufferedResourceUploadInfo(
         const std::string& buffer,
@@ -188,7 +192,8 @@ class LLNewBufferedResourceUploadInfo : public LLResourceUploadInfo
         U32 everyonePerms,
         S32 expectedCost,
         bool show_inventory,
-        uploadFinish_f finish);
+        uploadFinish_f finish,
+        uploadFailure_f failure);
 
     virtual LLSD        prepareUpload();
 
@@ -196,9 +201,11 @@ class LLNewBufferedResourceUploadInfo : public LLResourceUploadInfo
 
     virtual LLSD        exportTempFile();
     virtual LLUUID      finishUpload(LLSD &result);
+    virtual bool        failedUpload(LLSD &result, std::string &reason);
 
 private:
     uploadFinish_f  mFinishFn;
+    uploadFailure_f  mFailureFn;
     std::string mBuffer;
 };
 
@@ -208,14 +215,16 @@ class LLBufferedAssetUploadInfo : public LLResourceUploadInfo
 public:
     typedef std::function<void(LLUUID itemId, LLUUID newAssetId, LLUUID newItemId, LLSD response)> invnUploadFinish_f;
     typedef std::function<void(LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response)> taskUploadFinish_f;
+    typedef std::function<bool(LLUUID itemId, LLUUID taskId, LLSD response, std::string reason)> uploadFailed_f;
 
-    LLBufferedAssetUploadInfo(LLUUID itemId, LLAssetType::EType assetType, std::string buffer, invnUploadFinish_f finish);
+    LLBufferedAssetUploadInfo(LLUUID itemId, LLAssetType::EType assetType, std::string buffer, invnUploadFinish_f finish, uploadFailed_f failed);
     LLBufferedAssetUploadInfo(LLUUID itemId, LLPointer<LLImageFormatted> image, invnUploadFinish_f finish);
-    LLBufferedAssetUploadInfo(LLUUID taskId, LLUUID itemId, LLAssetType::EType assetType, std::string buffer, taskUploadFinish_f finish);
+    LLBufferedAssetUploadInfo(LLUUID taskId, LLUUID itemId, LLAssetType::EType assetType, std::string buffer, taskUploadFinish_f finish, uploadFailed_f failed);
 
     virtual LLSD        prepareUpload();
     virtual LLSD        generatePostBody();
     virtual LLUUID      finishUpload(LLSD &result);
+    virtual bool        failedUpload(LLSD &result, std::string &reason);
 
     LLUUID              getTaskId() const { return mTaskId; }
     const std::string & getContents() const { return mContents; }
@@ -232,6 +241,7 @@ class LLBufferedAssetUploadInfo : public LLResourceUploadInfo
     std::string         mContents;
     invnUploadFinish_f  mInvnFinishFn;
     taskUploadFinish_f  mTaskFinishFn;
+    uploadFailed_f      mFailureFn;
     bool                mStoredToCache;
 };
 
@@ -245,9 +255,9 @@ class LLScriptAssetUpload : public LLBufferedAssetUploadInfo
         MONO
     };
 
-    LLScriptAssetUpload(LLUUID itemId, std::string buffer, invnUploadFinish_f finish);
+    LLScriptAssetUpload(LLUUID itemId, std::string buffer, invnUploadFinish_f finish, uploadFailed_f failed);
     LLScriptAssetUpload(LLUUID taskId, LLUUID itemId, TargetType_t targetType, 
-            bool isRunning, LLUUID exerienceId, std::string buffer, taskUploadFinish_f finish);
+            bool isRunning, LLUUID exerienceId, std::string buffer, taskUploadFinish_f finish, uploadFailed_f failed);
 
     virtual LLSD        generatePostBody();