diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 70a3747b71b18dce10e5f5714dec4eb372359a9d..df49f5fa420d8c4a2136de4a89eb4b6a8715d93c 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -24,6 +24,8 @@ jobs:
     outputs:
       viewer_channel: ${{ steps.build.outputs.viewer_channel }}
       viewer_version: ${{ steps.build.outputs.viewer_version }}
+      viewer_branch:  ${{ steps.which-branch.outputs.branch }}
+      relnotes:       ${{ steps.which-branch.outputs.relnotes }}
       imagename: ${{ steps.build.outputs.imagename }}
     env:
       AUTOBUILD_ADDRSIZE: 64
@@ -334,7 +336,7 @@ jobs:
           version: ${{ needs.build.outputs.viewer_version }}
 
   release:
-    needs: [sign-and-package-windows, sign-and-package-mac]
+    needs: [build, sign-and-package-windows, sign-and-package-mac]
     runs-on: ubuntu-latest
     if: github.ref_type == 'tag' && startsWith(github.ref_name, 'Second_Life_')
     steps:
@@ -365,17 +367,31 @@ jobs:
           mv newview/viewer_version.txt macOS-viewer_version.txt
 
       # forked from softprops/action-gh-release
-      - uses: secondlife-3p/action-gh-release@v1
+      - name: Create GitHub release
+        id: release
+        uses: secondlife-3p/action-gh-release@v1
         with:
-          # name the release page for the build number so we can find it
-          # easily (analogous to looking up a codeticket build page)
-          name: "v${{ github.run_id }}"
+          # name the release page for the branch
+          name: "${{ needs.build.outputs.viewer_branch }}"
+          # SL-20546: want the channel and version to be visible on the
+          # release page
+          body: |
+            Build ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+            ${{ needs.build.outputs.viewer_channel }}
+            ${{ needs.build.outputs.viewer_version }}
+            ${{ needs.build.outputs.relnotes }}
           prerelease: true
           generate_release_notes: true
-          # the only reason we generate a GH release is to post build products
+          target_commitish: ${{ github.sha }}
+          previous_tag: release
+          append_body: true
           fail_on_unmatched_files: true
           files: |
             *.dmg 
             *.exe
             *-autobuild-package.xml
             *-viewer_version.txt
+
+      - name: post release URL
+        run: |
+          echo "::notice::Release ${{ steps.release.outputs.url }}"
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index 3ab97b557f051dd0518528b4c765504a56cb29e4..1d383f174df10c69e0b34481c13208c275cf9d23 100644
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -278,6 +278,7 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl
     catch (std::bad_alloc&)
     {
         // Out of memory on stack allocation?
+        LLError::LLUserWarningMsg::showOutOfMemory();
         printActiveCoroutines();
         LL_ERRS("LLCoros") << "Bad memory allocation in LLCoros::launch(" << prefix << ")!" << LL_ENDL;
     }
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 414515854a241be125a3d33ab5b662b6bb75b40d..3de641fcba6d2fc04d9d8637aa5176bb11f75d09 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -1601,6 +1601,48 @@ namespace LLError
     {
         return out << boost::stacktrace::stacktrace();
     }
+
+    // LLOutOfMemoryWarning
+    std::string LLUserWarningMsg::sLocalizedOutOfMemoryTitle;
+    std::string LLUserWarningMsg::sLocalizedOutOfMemoryWarning;
+    LLUserWarningMsg::Handler LLUserWarningMsg::sHandler;
+
+    void LLUserWarningMsg::show(const std::string& message)
+    {
+        if (sHandler)
+        {
+            sHandler(std::string(), message);
+        }
+    }
+
+    void LLUserWarningMsg::showOutOfMemory()
+    {
+        if (sHandler && !sLocalizedOutOfMemoryTitle.empty())
+        {
+            sHandler(sLocalizedOutOfMemoryTitle, sLocalizedOutOfMemoryWarning);
+        }
+    }
+
+    void LLUserWarningMsg::showMissingFiles()
+    {
+        // Files Are missing, likely can't localize.
+        const std::string error_string =
+            "Second Life viewer couldn't access some of the files it needs and will be closed."
+            "\n\nPlease reinstall viewer from  https://secondlife.com/support/downloads/ and "
+            "contact https://support.secondlife.com if issue persists after reinstall.";
+        sHandler("Missing Files", error_string);
+    }
+
+    void LLUserWarningMsg::setHandler(const LLUserWarningMsg::Handler &handler)
+    {
+        sHandler = handler;
+    }
+
+    void LLUserWarningMsg::setOutOfMemoryStrings(const std::string& title, const std::string& message)
+    {
+        sLocalizedOutOfMemoryTitle = title;
+        sLocalizedOutOfMemoryWarning = message;
+    }
 }
 
 void crashdriver(void (*callback)(int*))
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
index 05dd88ee514fc5a2097b7888d0744359e3699efd..6f6b349cf54c7b67043167c83fa68e86735a6658 100644
--- a/indra/llcommon/llerror.h
+++ b/indra/llcommon/llerror.h
@@ -39,6 +39,7 @@
 #include "llpreprocessor.h"
 
 #include <boost/static_assert.hpp>
+#include <functional> // std::function
 
 const int LL_ERR_NOERR = 0;
 
@@ -301,6 +302,28 @@ namespace LLError
     {
         friend std::ostream& operator<<(std::ostream& out, const LLStacktrace&);
     };
+
+    // Provides access to OS notification popup on error, since
+    // not everything has access to OS's messages
+    class LLUserWarningMsg
+    {
+    public:
+        typedef std::function<void(const std::string&, const std::string&)> Handler;
+        static void setHandler(const Handler&);
+        static void setOutOfMemoryStrings(const std::string& title, const std::string& message);
+
+        // When viewer encounters bad alloc or can't access files try warning user about reasons
+        static void showOutOfMemory();
+        static void showMissingFiles();
+        // Genering error
+        static void show(const std::string&);
+
+    private:
+        // needs to be preallocated before viewer runs out of memory
+        static std::string sLocalizedOutOfMemoryTitle;
+        static std::string sLocalizedOutOfMemoryWarning;
+        static Handler sHandler;
+    };
 }
 
 //this is cheaper than llcallstacks if no need to output other variables to call stacks. 
diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp
index 46560b5e4ce39ac7c12dc45b7ae1d080448090d7..0787bde57f101b02253d89cd5889dc82032ef24a 100644
--- a/indra/llcommon/llexception.cpp
+++ b/indra/llcommon/llexception.cpp
@@ -37,6 +37,7 @@
 #include "llerror.h"
 #include "llerrorcontrol.h"
 
+
 // used to attach and extract stacktrace information to/from boost::exception,
 // see https://www.boost.org/doc/libs/release/doc/html/stacktrace/getting_started.html#stacktrace.getting_started.exceptions_with_stacktrace
 // apparently the struct passed as the first template param needs no definition?
diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp
index 294acd7f638175501216323678e8d0cb59a9b929..517076804d2b40140d135b62018f8e0a772358a5 100644
--- a/indra/llcorehttp/_httpservice.cpp
+++ b/indra/llcorehttp/_httpservice.cpp
@@ -320,6 +320,7 @@ void HttpService::threadRun(LLCoreInt::HttpThread * thread)
             LLMemory::logMemoryInfo(TRUE);
 
             //output possible call stacks to log file.
+            LLError::LLUserWarningMsg::showOutOfMemory();
             LLError::LLCallStacks::print();
 
             LL_ERRS() << "Bad memory allocation in HttpService::threadRun()!" << LL_ENDL;
diff --git a/indra/llimage/llimagebmp.cpp b/indra/llimage/llimagebmp.cpp
index 90b7272efa78f17d4314f8b07a763b23f0ba3b23..cdea0da68ddad653d88f9439b7e2d60ae1786a86 100644
--- a/indra/llimage/llimagebmp.cpp
+++ b/indra/llimage/llimagebmp.cpp
@@ -321,6 +321,7 @@ bool LLImageBMP::updateData()
 		mColorPalette = new(std::nothrow) U8[color_palette_size];
 		if (!mColorPalette)
 		{
+            LLError::LLUserWarningMsg::showOutOfMemory();
 			LL_ERRS() << "Out of memory in LLImageBMP::updateData()" << LL_ENDL;
 			return false;
 		}
diff --git a/indra/llimage/llimagedxt.cpp b/indra/llimage/llimagedxt.cpp
index 36317a5ba883c39cd973549c308ba66a658e7f73..ae76c5243f0a4215f9563ab3a614294b0f05fac3 100644
--- a/indra/llimage/llimagedxt.cpp
+++ b/indra/llimage/llimagedxt.cpp
@@ -437,6 +437,7 @@ bool LLImageDXT::convertToDXR()
 	U8* newdata = (U8*)ll_aligned_malloc_16(total_bytes);
 	if (!newdata)
 	{
+        LLError::LLUserWarningMsg::showOutOfMemory();
 		LL_ERRS() << "Out of memory in LLImageDXT::convertToDXR()" << LL_ENDL;
 		return false;
 	}
diff --git a/indra/llimage/llimagetga.cpp b/indra/llimage/llimagetga.cpp
index 88bdae9b80f4fe3f1247759bb4a2f98a51a275ac..152a7f309c0417d9b91edf142937cea3a2e7307c 100644
--- a/indra/llimage/llimagetga.cpp
+++ b/indra/llimage/llimagetga.cpp
@@ -266,6 +266,7 @@ bool LLImageTGA::updateData()
 			mColorMap = new(std::nothrow) U8[ color_map_bytes ];  
 			if (!mColorMap)
 			{
+                LLError::LLUserWarningMsg::showOutOfMemory();
 				LL_ERRS() << "Out of Memory in bool LLImageTGA::updateData()" << LL_ENDL;
 				return false;
 			}
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index c6fd824c4eed881c7aa05ff0f391a44267d60237..56a12b07b1dfc32f7801b502088e7d73458666b8 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -1353,6 +1353,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
                 scratch = new(std::nothrow) U32[width * height];
                 if (!scratch)
                 {
+                    LLError::LLUserWarningMsg::showOutOfMemory();
                     LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
                               << " bytes for a manual image W" << width << " H" << height << LL_ENDL;
                 }
@@ -1378,6 +1379,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
                 scratch = new(std::nothrow) U32[width * height];
                 if (!scratch)
                 {
+                    LLError::LLUserWarningMsg::showOutOfMemory();
                     LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
                         << " bytes for a manual image W" << width << " H" << height << LL_ENDL;
                 }
@@ -1406,6 +1408,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
                 scratch = new(std::nothrow) U32[width * height];
                 if (!scratch)
                 {
+                    LLError::LLUserWarningMsg::showOutOfMemory();
                     LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
                         << " bytes for a manual image W" << width << " H" << height << LL_ENDL;
                 }
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index ee66122774d0487d1ed9fe3a85aa7de615a801d2..4d64dc9e104ab729f488091a4afd6b2dc1fe9b02 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -857,7 +857,7 @@ LLRender::~LLRender()
 	shutdown();
 }
 
-void LLRender::init(bool needs_vertex_buffer)
+bool LLRender::init(bool needs_vertex_buffer)
 {
 #if LL_WINDOWS
     if (gGLManager.mHasDebugOutput && gDebugGL)
@@ -879,6 +879,13 @@ void LLRender::init(bool needs_vertex_buffer)
     // necessary for reflection maps
     glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
 
+#if LL_WINDOWS
+    if (glGenVertexArrays == nullptr)
+    {
+        return false;
+    }
+#endif
+
     { //bind a dummy vertex array object so we're core profile compliant
         U32 ret;
         glGenVertexArrays(1, &ret);
@@ -889,6 +896,7 @@ void LLRender::init(bool needs_vertex_buffer)
     {
         initVertexBuffer();
     }
+    return true;
 }
 
 void LLRender::initVertexBuffer()
diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h
index fd922affbabe219dfda2ca9713abce09e89b0407..716b52354dc4fb0c2061e17e05af358f1f1b1e10 100644
--- a/indra/llrender/llrender.h
+++ b/indra/llrender/llrender.h
@@ -375,7 +375,7 @@ class LLRender
 
 	LLRender();
 	~LLRender();
-    void init(bool needs_vertex_buffer);
+    bool init(bool needs_vertex_buffer);
     void initVertexBuffer();
     void resetVertexBuffer();
 	void shutdown();
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index d736aa6634879ca33fc27023000f539c59db6a8f..2fe9ee18e3077b681ea060a3fa57aaa39479c8f1 100644
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -1553,6 +1553,7 @@ bool LLNotifications::loadTemplates()
 
 	if (!success || root.isNull() || !root->hasName( "notifications" ))
 	{
+        LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
 		LL_ERRS() << "Problem reading XML from UI Notifications file: " << base_filename << LL_ENDL;
 		return false;
 	}
@@ -1563,6 +1564,7 @@ bool LLNotifications::loadTemplates()
 
 	if(!params.validateBlock())
 	{
+        LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
 		LL_ERRS() << "Problem reading XUI from UI Notifications file: " << base_filename << LL_ENDL;
 		return false;
 	}
@@ -1629,6 +1631,7 @@ bool LLNotifications::loadVisibilityRules()
 
 	if(!params.validateBlock())
 	{
+        LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
 		LL_ERRS() << "Problem reading UI Notification Visibility Rules file: " << full_filename << LL_ENDL;
 		return false;
 	}
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 092739a5382be768abe58c22099328873d0b4aaf..a247e8700a58a587931f7af7a9bd7ddb595bd821 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -2694,6 +2694,7 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
 	char* text = new char[ text_len + 1];
 	if (text == NULL)
 	{
+        LLError::LLUserWarningMsg::showOutOfMemory();
 		LL_ERRS() << "Memory allocation failure." << LL_ENDL;			
 		return FALSE;
 	}
diff --git a/indra/llui/lltransutil.cpp b/indra/llui/lltransutil.cpp
index 5da722a72bb8fd1815c5ee3ddcdb4cb58926f765..6c486f29ba94ebae5d824bc89a4ae79a07a5b9f9 100644
--- a/indra/llui/lltransutil.cpp
+++ b/indra/llui/lltransutil.cpp
@@ -44,8 +44,13 @@ bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set<s
 	bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root, LLDir::ALL_SKINS);
 	if (!success)
 	{
+        const std::string error_string =
+            "Second Life viewer couldn't access some of the files it needs and will be closed."
+            "\n\nPlease reinstall viewer from  https://secondlife.com/support/downloads/ and "
+            "contact https://support.secondlife.com if issue persists after reinstall.";
+        LLError::LLUserWarningMsg::show(error_string);
 		gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
-		LL_ERRS() << "Couldn't load string table " << xml_filename << ". Please reinstall viewer from  https://secondlife.com/support/downloads/ and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL;
+		LL_ERRS() << "Couldn't load string table " << xml_filename << " " << errno << LL_ENDL;
 		return false;
 	}
 
@@ -60,6 +65,7 @@ bool LLTransUtil::parseLanguageStrings(const std::string& xml_filename)
 	
 	if (!success)
 	{
+        LLError::LLUserWarningMsg::showMissingFiles();
 		LL_ERRS() << "Couldn't load localization table " << xml_filename << LL_ENDL;
 		return false;
 	}
diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp
index debf93dccd740753599de90fdc3adf635176e3a7..51e259992d25bf238313d653600810eefe815727 100644
--- a/indra/newview/llappcorehttp.cpp
+++ b/indra/newview/llappcorehttp.cpp
@@ -168,6 +168,7 @@ void LLAppCoreHttp::init()
     }
     else
     {
+        LLError::LLUserWarningMsg::showMissingFiles();
         LL_ERRS("Init") << "Missing CA File; should be at " << ca_file << LL_ENDL;
     }
     
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 28dd2a3c9abf33bb536e2f3dee1b938d6ec64745..eb62fe0c182698d7e71595a1af7da8e85c386b48 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -363,7 +363,6 @@ BOOL gRandomizeFramerate = FALSE;
 BOOL gPeriodicSlowFrame = FALSE;
 
 BOOL gCrashOnStartup = FALSE;
-BOOL gLLErrorActivated = FALSE;
 BOOL gLogoutInProgress = FALSE;
 
 BOOL gSimulateMemLeak = FALSE;
@@ -2254,9 +2253,6 @@ void errorCallback(LLError::ELevel level, const std::string &error_string)
         OSMessageBox(error_string, LLTrans::getString("MBFatalError"), OSMB_OK);
 #endif
 
-        //Set the ErrorActivated global so we know to create a marker file
-        gLLErrorActivated = true;
-
         gDebugInfo["FatalMessage"] = error_string;
         // We're not already crashing -- we simply *intend* to crash. Since we
         // haven't actually trashed anything yet, we can afford to write the whole
@@ -2265,6 +2261,14 @@ void errorCallback(LLError::ELevel level, const std::string &error_string)
     }
 }
 
+void errorMSG(const std::string& title_string, const std::string& message_string)
+{
+    if (!message_string.empty())
+    {
+        OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK);
+    }
+}
+
 void LLAppViewer::initLoggingAndGetLastDuration()
 {
     //
@@ -2276,6 +2280,8 @@ void LLAppViewer::initLoggingAndGetLastDuration()
     LLError::addGenericRecorder(&errorCallback);
     //LLError::setTimeFunction(getRuntime);
 
+    LLError::LLUserWarningMsg::setHandler(errorMSG);
+
 
     if (mSecondInstance)
     {
@@ -2413,6 +2419,7 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key,
 			{	// failed to load
 				if(file.required)
 				{
+                    LLError::LLUserWarningMsg::showMissingFiles();
 					LL_ERRS() << "Error: Cannot load required settings file from: " << full_settings_path << LL_ENDL;
 					return false;
 				}
@@ -2511,6 +2518,7 @@ bool LLAppViewer::initConfiguration()
 	if (!success)
 	{
         LL_WARNS() << "Cannot load default configuration file " << settings_file_list << LL_ENDL;
+        LLError::LLUserWarningMsg::showMissingFiles();
         if (gDirUtilp->fileExists(settings_file_list))
         {
             LL_ERRS() << "Cannot load default configuration file settings_files.xml. "
@@ -2534,6 +2542,7 @@ bool LLAppViewer::initConfiguration()
 
 	if (!mSettingsLocationList->validateBlock())
 	{
+        LLError::LLUserWarningMsg::showMissingFiles();
         LL_ERRS() << "Invalid settings file list " << settings_file_list << LL_ENDL;
 	}
 
@@ -2968,6 +2977,8 @@ bool LLAppViewer::initConfiguration()
 		LLEventPumps::instance().obtain("LLControlGroup").post(LLSDMap("init", key));
 	}
 
+    LLError::LLUserWarningMsg::setOutOfMemoryStrings(LLTrans::getString("MBOutOfMemoryTitle"), LLTrans::getString("MBOutOfMemoryErr"));
+
 	return true; // Config was successful.
 }
 
@@ -3005,6 +3016,7 @@ void LLAppViewer::initStrings()
 
 		// initial check to make sure files are there failed
 		gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
+        LLError::LLUserWarningMsg::showMissingFiles();
 		LL_ERRS() << "Viewer failed to find localization and UI files."
 			<< " Please reinstall viewer from https://secondlife.com/support/downloads"
 			<< " and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL;
@@ -4310,6 +4322,7 @@ void LLAppViewer::loadKeyBindings()
 		key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "key_bindings.xml");
 		if (!gViewerInput.loadBindingsXML(key_bindings_file))
 		{
+            LLError::LLUserWarningMsg::showMissingFiles();
 			LL_ERRS("InitInfo") << "Unable to open default key bindings from " << key_bindings_file << LL_ENDL;
 		}
 	}
@@ -5400,6 +5413,14 @@ void LLAppViewer::forceErrorLLError()
    	LL_ERRS() << "This is a deliberate llerror" << LL_ENDL;
 }
 
+void LLAppViewer::forceErrorLLErrorMsg()
+{
+    LLError::LLUserWarningMsg::show("Deliberate error");
+    // Note: under debug this will show a message as well,
+    // but release won't show anything and will quit silently
+    LL_ERRS() << "This is a deliberate llerror with a message" << LL_ENDL;
+}
+
 void LLAppViewer::forceErrorBreakpoint()
 {
    	LL_WARNS() << "Forcing a deliberate breakpoint" << LL_ENDL;
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index 6d1496d51709c5383bcb64522bdaad751d730304..77a1cdb48585d40aed4f316e3293406ca964f382 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -152,6 +152,7 @@ class LLAppViewer : public LLApp
     // LLAppViewer testing helpers.
     // *NOTE: These will potentially crash the viewer. Only for debugging.
     virtual void forceErrorLLError();
+    virtual void forceErrorLLErrorMsg();
     virtual void forceErrorBreakpoint();
     virtual void forceErrorBadMemoryAccess();
     virtual void forceErrorInfiniteLoop();
diff --git a/indra/newview/llavatarrenderinfoaccountant.cpp b/indra/newview/llavatarrenderinfoaccountant.cpp
index a6c9a41fa46e0bf8d0e85b28b9829b00c4072b36..b95b9718906c57373b3fabdb4fd0c79a73a6ec81 100644
--- a/indra/newview/llavatarrenderinfoaccountant.cpp
+++ b/indra/newview/llavatarrenderinfoaccountant.cpp
@@ -339,6 +339,7 @@ void LLAvatarRenderInfoAccountant::sendRenderInfoToRegion(LLViewerRegion * regio
         }
         catch (std::bad_alloc&)
         {
+            LLError::LLUserWarningMsg::showOutOfMemory();
             LL_ERRS() << "LLCoros::launch() allocation failure" << LL_ENDL;
         }
 	}
@@ -370,6 +371,7 @@ void LLAvatarRenderInfoAccountant::getRenderInfoFromRegion(LLViewerRegion * regi
         }
         catch (std::bad_alloc&)
         {
+            LLError::LLUserWarningMsg::showOutOfMemory();
             LL_ERRS() << "LLCoros::launch() allocation failure" << LL_ENDL;
         }
 	}
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 01d6469010c6daee1fe821a957233cbfd1d7a092..6dc4f25d029aa9329133af3413211f3a72b901f5 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -1360,7 +1360,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
 				U8* buffer = new(std::nothrow) U8[size];
 				if (!buffer)
 				{
-					LL_WARNS_ONCE(LOG_MESH) << "Failed to allocate memory for skin info, size: " << size << LL_ENDL;
+					LL_WARNS(LOG_MESH) << "Failed to allocate memory for skin info, size: " << size << LL_ENDL;
 					return false;
 				}
 				LLMeshRepository::sCacheBytesRead += size;
@@ -1473,7 +1473,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 				U8* buffer = new(std::nothrow) U8[size];
 				if (!buffer)
 				{
-					LL_WARNS_ONCE(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL;
+					LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL;
 					return false;
 				}
 				LLMeshRepository::sCacheBytesRead += size;
@@ -1575,7 +1575,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 				U8* buffer = new(std::nothrow) U8[size];
 				if (!buffer)
 				{
-					LL_WARNS_ONCE(LOG_MESH) << "Failed to allocate memory for physics shape, size: " << size << LL_ENDL;
+					LL_WARNS(LOG_MESH) << "Failed to allocate memory for physics shape, size: " << size << LL_ENDL;
 					return false;
 				}
 				file.read(buffer, size);
@@ -1770,7 +1770,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 				U8* buffer = new(std::nothrow) U8[size];
 				if (!buffer)
 				{
-					LL_WARNS_ONCE(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL;
+					LL_WARNS(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL;
 					// todo: for now it will result in indefinite constant retries, should result in timeout
 					// or in retry-count and disabling mesh. (but usually viewer is beyond saving at this point)
 					return false;
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 972aac0bafe484dbe1b4f0bb7a6b1f6f1b61b4ab..d5e4de03a964138296122a7edb83331bc89c9879 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -289,6 +289,7 @@ void handle_disconnect_viewer(void *);
 
 void force_error_breakpoint(void *);
 void force_error_llerror(void *);
+void force_error_llerror_msg(void*);
 void force_error_bad_memory_access(void *);
 void force_error_infinite_loop(void *);
 void force_error_software_exception(void *);
@@ -2414,6 +2415,15 @@ class LLAdvancedForceErrorLlerror : public view_listener_t
 	}
 };
 
+class LLAdvancedForceErrorLlerrorMsg: public view_listener_t
+{
+    bool handleEvent(const LLSD& userdata)
+    {
+        force_error_llerror_msg(NULL);
+        return true;
+    }
+};
+
 class LLAdvancedForceErrorBadMemoryAccess : public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
@@ -8393,6 +8403,11 @@ void force_error_llerror(void *)
     LLAppViewer::instance()->forceErrorLLError();
 }
 
+void force_error_llerror_msg(void*)
+{
+    LLAppViewer::instance()->forceErrorLLErrorMsg();
+}
+
 void force_error_bad_memory_access(void *)
 {
     LLAppViewer::instance()->forceErrorBadMemoryAccess();
@@ -9619,6 +9634,7 @@ void initialize_menus()
 	// Advanced > Debugging
 	view_listener_t::addMenu(new LLAdvancedForceErrorBreakpoint(), "Advanced.ForceErrorBreakpoint");
 	view_listener_t::addMenu(new LLAdvancedForceErrorLlerror(), "Advanced.ForceErrorLlerror");
+    view_listener_t::addMenu(new LLAdvancedForceErrorLlerrorMsg(), "Advanced.ForceErrorLlerrorMsg");
 	view_listener_t::addMenu(new LLAdvancedForceErrorBadMemoryAccess(), "Advanced.ForceErrorBadMemoryAccess");
 	view_listener_t::addMenu(new LLAdvancedForceErrorBadMemoryAccessCoro(), "Advanced.ForceErrorBadMemoryAccessCoro");
 	view_listener_t::addMenu(new LLAdvancedForceErrorInfiniteLoop(), "Advanced.ForceErrorInfiniteLoop");
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 58024fb60e550a78f4cd1fd45c425ab44338d93d..1459686f104b578f96fda7a34a819a62364fe6cb 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -1957,7 +1957,11 @@ LLViewerWindow::LLViewerWindow(const Params& p)
 	// Initialize OpenGL Renderer
 	LLVertexBuffer::initClass(mWindow);
 	LL_INFOS("RenderInit") << "LLVertexBuffer initialization done." << LL_ENDL ;
-	gGL.init(true);
+	if (!gGL.init(true))
+    {
+        LLError::LLUserWarningMsg::show(LLTrans::getString("MBVideoDrvErr"));
+        LL_ERRS() << "gGL not initialized" << LL_ENDL;
+    }
 
 	if (LLFeatureManager::getInstance()->isSafe()
 		|| (gSavedSettings.getS32("LastFeatureVersion") != LLFeatureManager::getInstance()->getVersion())
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 1d012cd348631cfbc282adb014b4197674a9d223..4719b091ab4f89b57f049c5c294440f1058da2a4 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2716,6 +2716,12 @@ function="World.EnvPreset"
                 <menu_item_call.on_click
                  function="Advanced.ForceErrorLlerror" />
             </menu_item_call>
+            <menu_item_call
+             label="Force LLError, Message And Crash"
+             name="Force LLError And Crash">
+                <menu_item_call.on_click
+                 function="Advanced.ForceErrorLlerrorMsg" />
+            </menu_item_call>
             <menu_item_call
              label="Force Bad Memory Access"
              name="Force Bad Memory Access">
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index f3b26820d2b80ddecfb077537cc780eb4c238aa1..7360c06f86145de580426b4dcb22f5aae907bbfe 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -3019,8 +3019,19 @@ Running in window.
 
 If you continue to receive this message, contact the [SUPPORT_SITE].
 	</string>
+    <string name="MBOutOfMemoryTitle">Out Of Memory</string>
+    <string name="MBOutOfMemoryErr">
+        [APP_NAME]'s request for memory failed. Application can't proceed and will be closed.
 
-	<!-- Avatar Shape Information -->
+If your computer's RAM is low, quit any heavy applications before runing Second Life, allocate a page file or reduce graphical settings like draw distance.
+    </string>
+    <string name="MBMissingFile">
+        [APP_NAME] couldn't access or find some of the files it needs and will be closed.
+
+Please reinstall viewer from  https://secondlife.com/support/downloads/ and contact https://support.secondlife.com if issue persists after reinstall.
+    </string>
+
+    <!-- Avatar Shape Information -->
 <string name="5 O'Clock Shadow">5 O'Clock Shadow</string>
 
 <string name="All White">All White</string>