diff --git a/core_info.c b/core_info.c
index 69075fe98f..ea2ab05201 100644
--- a/core_info.c
+++ b/core_info.c
@@ -732,17 +732,18 @@ end:
    return core_info_cache_list;
 }
 
-static void core_info_cache_write(core_info_cache_list_t *list, const char *info_dir)
+static bool core_info_cache_write(core_info_cache_list_t *list, const char *info_dir)
 {
    intfstream_t *file    = NULL;
    rjsonwriter_t *writer = NULL;
+   bool success          = false;
    char file_path[PATH_MAX_LENGTH];
    size_t i, j;
 
    file_path[0] = '\0';
 
    if (!list)
-      return;
+      return false;
 
    /* Open info cache file */
    if (string_is_empty(info_dir))
@@ -763,7 +764,7 @@ static void core_info_cache_write(core_info_cache_list_t *list, const char *info
    if (!file)
    {
       RARCH_ERR("[Core Info] Failed to write to core info cache file: %s\n", file_path);
-      return;
+      return false;
    }
 
    /* Write info cache */
@@ -1068,6 +1069,7 @@ static void core_info_cache_write(core_info_cache_list_t *list, const char *info
    rjsonwriter_free(writer);
 
    RARCH_LOG("[Core Info] Wrote to cache file: %s\n", file_path);
+   success = true;
 
    /* Remove 'force refresh' file, if required */
    file_path[0] = '\0';
@@ -1086,6 +1088,7 @@ end:
    free(file);
 
    list->refresh = false;
+   return success;
 }
 
 static void core_info_check_uninstalled(core_info_cache_list_t *list)
@@ -1774,7 +1777,8 @@ static core_info_list_t *core_info_list_new(const char *path,
       const char *libretro_info_dir,
       const char *exts,
       bool dir_show_hidden_files,
-      bool enable_cache)
+      bool enable_cache,
+      bool *cache_supported)
 {
    size_t i;
    core_path_list_t *path_list                  = NULL;
@@ -1906,12 +1910,14 @@ static core_info_list_t *core_info_list_new(const char *path,
     *   a refresh)
     * > Write new cache to disk if updates are
     *   required */
+   *cache_supported = true;
    if (core_info_cache_list)
    {
       core_info_check_uninstalled(core_info_cache_list);
 
       if (core_info_cache_list->refresh)
-         core_info_cache_write(core_info_cache_list, info_dir);
+         *cache_supported = core_info_cache_write(
+               core_info_cache_list, info_dir);
 
       core_info_cache_list_free(core_info_cache_list);
    }
@@ -2104,14 +2110,16 @@ void core_info_deinit_list(void)
 }
 
 bool core_info_init_list(const char *path_info, const char *dir_cores,
-      const char *exts, bool dir_show_hidden_files, bool enable_cache)
+      const char *exts, bool dir_show_hidden_files,
+      bool enable_cache, bool *cache_supported)
 {
    core_info_state_t *p_coreinfo = coreinfo_get_ptr();
    if (!(p_coreinfo->curr_list = core_info_list_new(dir_cores,
                !string_is_empty(path_info) ? path_info : dir_cores,
                exts,
                dir_show_hidden_files,
-               enable_cache)))
+               enable_cache,
+               cache_supported)))
       return false;
    return true;
 }
diff --git a/core_info.h b/core_info.h
index 7cb286abff..ae9a7f28ab 100644
--- a/core_info.h
+++ b/core_info.h
@@ -157,7 +157,8 @@ bool core_info_get_current_core(core_info_t **core);
 void core_info_deinit_list(void);
 
 bool core_info_init_list(const char *path_info, const char *dir_cores,
-      const char *exts, bool show_hidden_files, bool enable_cache);
+      const char *exts, bool show_hidden_files,
+      bool enable_cache, bool *cache_supported);
 
 bool core_info_get_list(core_info_list_t **core);
 
diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h
index c36435fc05..dc77aff722 100644
--- a/intl/msg_hash_us.h
+++ b/intl/msg_hash_us.h
@@ -12034,6 +12034,10 @@ MSG_HASH(
    MSG_UNSUPPORTED_VIDEO_MODE,
    "Unsupported video mode"
    )
+MSG_HASH(
+   MSG_CORE_INFO_CACHE_UNSUPPORTED,
+   "Cannot write to core info directory - core info cache will be disabled"
+   )
 
 /* Lakka */
 
diff --git a/menu/menu_setting.c b/menu/menu_setting.c
index d65711c726..bc50449ed5 100644
--- a/menu/menu_setting.c
+++ b/menu/menu_setting.c
@@ -7959,8 +7959,24 @@ static void general_write_handler(rarch_setting_t *setting)
              * force a cache refresh on the next
              * core info initialisation */
             if (*setting->value.target.boolean)
-               core_info_cache_force_refresh(!string_is_empty(path_libretro_info) ?
-                     path_libretro_info : dir_libretro);
+               if (!core_info_cache_force_refresh(!string_is_empty(path_libretro_info) ?
+                     path_libretro_info : dir_libretro))
+               {
+                  /* core_info_cache_force_refresh() will fail
+                   * if we cannot write to the the core_info
+                   * directory. This will typically only happen
+                   * on platforms where the core_info directory
+                   * is explicitly (and intentionally) placed on
+                   * read-only storage. In this case, core info
+                   * caching cannot function correctly anyway,
+                   * so we simply force-disable the feature */
+                  configuration_set_bool(settings,
+                        settings->bools.core_info_cache_enable, false);
+                  runloop_msg_queue_push(
+                        msg_hash_to_str(MSG_CORE_INFO_CACHE_UNSUPPORTED),
+                        1, 100, true,
+                        NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
+               }
          }
          break;
       default:
diff --git a/msg_hash.h b/msg_hash.h
index 7ae1857157..0f9f8c35df 100644
--- a/msg_hash.h
+++ b/msg_hash.h
@@ -491,6 +491,7 @@ enum msg_hash_enums
    MSG_LOCALAP_ERROR_CONFIG_PARSE,
 #endif
    MSG_UNSUPPORTED_VIDEO_MODE,
+   MSG_CORE_INFO_CACHE_UNSUPPORTED,
 
    MENU_LABEL(MENU_XMB_ANIMATION_HORIZONTAL_HIGHLIGHT),
    MENU_LABEL(MENU_XMB_ANIMATION_MOVE_UP_DOWN),
diff --git a/retroarch.c b/retroarch.c
index 14b126adef..15c67eeb77 100644
--- a/retroarch.c
+++ b/retroarch.c
@@ -14296,12 +14296,25 @@ bool command_event(enum event_command cmd, void *data)
                return false;
 
             if (!string_is_empty(dir_libretro))
+            {
+               bool cache_supported = false;
+
                core_info_init_list(path_libretro_info,
                      dir_libretro,
                      ext_name,
                      show_hidden_files,
-                     core_info_cache_enable
-                     );
+                     core_info_cache_enable,
+                     &cache_supported);
+
+               /* If core info cache is enabled but cache
+                * functionality is unsupported (i.e. because
+                * the core info directory is on read-only
+                * storage), force-disable the setting to
+                * avoid repeated failures */
+               if (core_info_cache_enable && !cache_supported)
+                  configuration_set_bool(settings,
+                        settings->bools.core_info_cache_enable, false);
+            }
          }
          break;
       case CMD_EVENT_CORE_DEINIT: